diff options
Diffstat (limited to 'odb-tests/common')
386 files changed, 38622 insertions, 0 deletions
diff --git a/odb-tests/common/access/buildfile b/odb-tests/common/access/buildfile new file mode 100644 index 0000000..cbdcab7 --- /dev/null +++ b/odb-tests/common/access/buildfile @@ -0,0 +1,45 @@ +# file : common/access/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_access_ \ + --generate-schema \ + --accessor-regex '#(.+)#Get\u\1#' \ + --modifier-regex '#(.+)#Set\u\1#' + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +# @@ BUILD2: Eventually we should be able to mark it as test.input once +# this is supported for testscript tests. +# +exe{driver}: ../../alias{database-client}: include = adhoc diff --git a/odb-tests/common/access/driver.cxx b/odb-tests/common/access/driver.cxx new file mode 100644 index 0000000..b40e73c --- /dev/null +++ b/odb-tests/common/access/driver.cxx @@ -0,0 +1,262 @@ +// file : common/access/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test accessor/modifier expressions. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic accessor/modifier functionality. + // + { + using namespace test1; + + object o (1, 623, 723); + o.i1 () = 123; + o.i2 (223); + o.i3 () = 323; + o.i4 () = 423; + o.set_i5 (523); + o.s1 ("1bc"); + memcpy (o.b1 (), "123456789012345", 16); + o.b2 ("123456789012345"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id ())); + t.commit (); + + assert (o == *p); + } + } + + // Test composite accessor/modifier functionality. + // + { + using namespace test2; + + object o (1); + + o.v1 () = value (1123, 1234); + o.v2 (value (2123, 2234)); + o.v3_i1 (3123); + o.v3_i2 (3223); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id ())); + t.commit (); + + assert (o == *p); + } + } + + // Test object pointer accessor/modifier functionality. + // + { + using namespace test3; + + object2 o (1); + o.p1 ().reset (new object1 (1)); + o.p2 (object1_ptr (new object1 (2))); + + { + transaction t (db->begin ()); + const object1_ptr& ptr (o.p1 ()); + db->persist (ptr); + db->persist (o.p2 ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> p (db->load<object2> (o.id ())); + t.commit (); + + assert (p->p1 ()->id () == o.p1 ()->id () && + p->p2 ()->id () == o.p2 ()->id ()); + } + } + + // Test container accessor/modifier functionality. + // + { + using namespace test4; + + object o (1); + o.c1 ().push_back (1123); + o.c1 ().push_back (1124); + o.c1 ().push_back (1125); + + { + std::vector<int> v; + v.push_back (2123); + v.push_back (2124); + v.push_back (2125); + o.c2 (v); + } + + o.v1 ().c1 ().push_back (1123); + o.v1 ().c1 ().push_back (1124); + o.v1 ().c1 ().push_back (1125); + + { + std::vector<int> v; + v.push_back (2123); + v.push_back (2124); + v.push_back (2125); + o.v1 ().c2 (v); + } + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id ())); + t.commit (); + + assert (o == *p); + } + } + + // Test id accessor/modifier functionality. + // + { + using namespace test5; + + object1 o1; + object2 o2; + object3 o3; + object4 o4; + o4.id (uuid ("\x60\x1D\x17\xF0-\x60\x05-\x47\x23-\x95\x37-" + "\xC1\xF8\x94\x41\x2B\xEC")); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + db->persist (o4); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object1> p1 (db->load<object1> (o1.id ())); + unique_ptr<object2> p2 (db->load<object2> (o2.id ())); + unique_ptr<object3> p3 (db->load<object3> (o3.id_)); + unique_ptr<object4> p4 (db->load<object4> (o4.id ())); + t.commit (); + } + } + + // Test version accessor/modifier functionality. + // + { + using namespace test6; + + object1 o1 (1); + object2 o2; + object3 o3 (1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + t.commit (); + + assert (o1.version () == 1); + assert (o2.version () == 1); + assert (o3.version_ == 1); + } + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + db->update (o3); + t.commit (); + + assert (o1.version () == 2); + assert (o2.version () == 2); + assert (o3.version_ == 2); + } + } + + // Test basic accessor/modifier functionality. + // + { + using namespace test7; + + object o (1); + o.i1 () = 123; + o.set_i2 (223); + o.setI3 (323); + o.seti4 (423); + o.i5 () = 523; + o.i6 () = 623; + o.SetI7 (723); + memcpy (o.b1 (), "123456789012345", 16); + o.b2 ("123456789012345"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id_)); + t.commit (); + + assert (o == *p); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/access/test.hxx b/odb-tests/common/access/test.hxx new file mode 100644 index 0000000..3a3424d --- /dev/null +++ b/odb-tests/common/access/test.hxx @@ -0,0 +1,592 @@ +// file : common/access/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> +#include <cstring> // std::memcpy, std::memcmp, std::memset +#include <memory> // std::unique_ptr +#include <utility> // std::move + +#include <odb/core.hxx> + +#ifdef ODB_COMPILER +# if defined(ODB_DATABASE_MYSQL) +# define BINARY16_TYPE "BINARY(16)" +# elif defined(ODB_DATABASE_SQLITE) +# define BINARY16_TYPE "BLOB" +# elif defined(ODB_DATABASE_PGSQL) +# define BINARY16_TYPE "BYTEA" +# elif defined(ODB_DATABASE_ORACLE) +# define BINARY16_TYPE "RAW(16)" +# elif defined(ODB_DATABASE_MSSQL) +# define BINARY16_TYPE "BINARY(16)" +# elif defined(ODB_DATABASE_COMMON) +# define BINARY16_TYPE "" +# else +# error unknown database +# endif +#endif + +// Test basic accessor/modifier functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object + struct object + { + object (): i6_ (0), i7_ (0) {} + object (unsigned long id, int i6, int i7): id_ (id), i6_ (i6), i7_ (i7) {} + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id access(id) + unsigned long id_; + + public: + int i1 () const {return i1_;} + int& i1 () {return i1_;} + private: + #pragma db access(i1) + int i1_; + + public: + int i2 () const {return i2_;} + void i2 (int i2) {i2_ = i2;} + private: + #pragma db access(i2) + int i2_; + + // Prefer reference modifier. + // + public: + int i3 () const {return i3_;} + int& i3 () {return i3_;} + void i3 (int i3); + private: + #pragma db access(i3) + int i3_; + + // Prefer reference modifier (reverse function order). + // + public: + int i4 () const {return i4_;} + void i4 (int i4); + int& i4 () {return i4_;} + private: + #pragma db access(i4) + int i4_; + + public: + int get_i5 () const {return i5_;} + void set_i5 (int i5) {i5_ = i5;} + private: + #pragma db get(get_i5) set(set_i5) + int i5_; + + // Const member via reference. + // + public: + const int& i6 () const {return i6_;} + private: + #pragma db get(i6) set(const_cast<int&> (this.i6 ())) + const int i6_; + + // Const member via modifier. + // + public: + int i7 () const {return i7_;} + void i7 (int i7) const {const_cast<int&> (i7_) = i7;} + private: + #pragma db access(i7) + const int i7_; + + public: + const char* s1 () const {return s1_.c_str ();} + void s1 (const char* s1) {s1_ = s1;} + //std::string s1 () const {return s1_;} + //void s1 (std::string s1) {s1_ = s1;} + private: + #pragma db get(s1) set(s1((?).c_str ())) + //#pragma db access(s1) + std::string s1_; + + // Array member via ref. + // + public: + const char* b1 () const {return b1_;} + char* b1 () {return b1_;} + private: + #pragma db type(BINARY16_TYPE) access(b1) + char b1_[16]; + + // Array member via modifier. + // + public: + const char* b2 () const {return b2_;} + void b2 (const char* b2) {std::memcpy (b2_, b2, sizeof (b2_));} + private: + #pragma db type(BINARY16_TYPE) access(b2) + char b2_[16]; + + public: + bool operator== (const object& o) const + { + return id_ == o.id_ && + i1_ == o.i1_ && + i2_ == o.i2_ && + i3_ == o.i3_ && + i4_ == o.i4_ && + i5_ == o.i5_ && + i6_ == o.i6_ && + i7_ == o.i7_ && + s1_ == o.s1_ && + std::memcmp (b1_, o.b1_, sizeof (b1_)) == 0 && + std::memcmp (b2_, o.b2_, sizeof (b2_)) == 0; + } + }; +} + +// Test composite accessor/modifier functionality. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db value + struct value + { + value () {} + value (int i1, int i2): i1_ (i1), i2_ (i2) {} + + bool operator== (const value& v) const + { + return i1_ == v.i1_ && i2_ == v.i2_; + } + + public: + int i1 () const {return i1_;} + int& i1 () {return i1_;} + private: + #pragma db access(i1) + int i1_; + + public: + int i2 () const {return i2_;} + void i2 (int i2) {i2_ = i2;} + private: + #pragma db access(i2) + int i2_; + }; + + #pragma db object + struct object + { + object () {} + object (unsigned long id): id_ (id) {} + + bool operator== (const object& o) const + { + return id_ == o.id_ && + v1_ == o.v1_ && + v2_ == o.v2_ && + v3_ == o.v3_; + } + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id access(id) + unsigned long id_; + + public: + const value& v1 () const {return v1_;} + value& v1 () {return v1_;} + private: + #pragma db access(v1) + value v1_; + + public: + const value& v2 () const {return v2_;} + void v2 (const value& v2) {v2_ = v2;} + private: + #pragma db access(v2) + value v2_; + + public: + int v3_i1 () const {return v3_.i1 ();} + int v3_i2 () const {return v3_.i2 ();} + void v3_i1 (int i1) {v3_.i1 () = i1;} + void v3_i2 (int i2) {v3_.i2 (i2);} + private: + #pragma db get(test2::value (this.v3_i1 (), this.v3_i2 ())) \ + set(this.v3_i1 ((?).i1 ()); this.v3_i2 ((?).i2 ())) + value v3_; + }; +} + +// Test object pointer accessor/modifier functionality. +// +#pragma db namespace table("t3_") +namespace test3 +{ + struct object1; + + typedef std::unique_ptr<object1> object1_ptr; + + #pragma db object pointer(object1_ptr) + struct object1 + { + object1 () {} + object1 (unsigned long id): id_ (id) {} + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id access(id) + unsigned long id_; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (unsigned long id): id_ (id) {} + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id access(id) + unsigned long id_; + + public: + const object1_ptr& p1 () const {return p1_;} + object1_ptr& p1 () {return p1_;} + private: + #pragma db access(p1) + object1_ptr p1_; + + public: + const object1_ptr& p2 () const {return p2_;} + + void p2 (object1_ptr p2) {p2_ = std::move (p2);} + + private: + #pragma db get(p2) set(p2 (std::move (?))) + + object1_ptr p2_; + }; +} + +// Test container accessor/modifier functionality. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db value + struct value + { + value (): c3_ (3, 999) {} + value (int v): c1_ (3, v), c2_ (3, v + 1), c3_ (3, v + 2) {} + + bool operator== (const value& v) const + { + return c1_ == v.c1_ && c2_ == v.c2_ && c3_ == v.c3_; + } + + public: + const std::vector<int>& c1 () const {return c1_;} + std::vector<int>& c1 () {return c1_;} + private: + #pragma db access(c1) + std::vector<int> c1_; + + public: + const std::vector<int>& c2 () const {return c2_;} + void c2 (const std::vector<int>& c2) {c2_ = c2;} + private: + #pragma db access(c2) + std::vector<int> c2_; + + public: + const std::vector<int> c3_; + }; + + #pragma db object + struct object + { + object () {} + object (unsigned long id): id_ (id), c3_ (3, 3123), v2_ (2123) {} + + bool operator== (const object& o) const + { + return id_ == o.id_ && + c1_ == o.c1_ && + c2_ == o.c2_ && + c3_ == o.c3_ && + v1_ == o.v1_ && + v2_ == o.v2_; + } + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id access(id) + unsigned long id_; + + public: + const std::vector<int>& c1 () const {return c1_;} + std::vector<int>& c1 () {return c1_;} + private: + #pragma db access(c1) + std::vector<int> c1_; + + public: + const std::vector<int>& c2 () const {return c2_;} + void c2 (const std::vector<int>& c2) {c2_ = c2;} + private: + #pragma db access(c2) + std::vector<int> c2_; + + public: + const std::vector<int>& c3 () const {return c3_;} + private: + #pragma db get(c3) set(const_cast<std::vector<int>&> (this.c3 ())) + const std::vector<int> c3_; + + public: + const value& v1 () const {return v1_;} + value& v1 () {return v1_;} + private: + #pragma db access(v1) + value v1_; + + public: + const value v2_; + }; +} + +// Test id accessor/modifier functionality. +// +#pragma db namespace table("t5_") +namespace test5 +{ + #pragma db object + struct object1 + { + object1 (): id_ (0) {} + + public: + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id auto access(id) + unsigned long id_; + }; + + #pragma db object + struct object2 + { + object2 (): id_ (0) {} + + public: + unsigned long id () const {return id_;} + unsigned long& id () {return id_;} + private: + #pragma db id auto access(id) + unsigned long id_; + }; + + #pragma db object + struct object3 + { + object3 (): id_ (0) {} + + #pragma db id auto + const unsigned long id_; + }; + + #pragma db value + struct uuid + { + uuid () {std::memset (data_, 0, sizeof (data_));} + explicit uuid (const char* d) {data (d);} + + public: + const char* data () const {return data_;} + void data (const char* d) {std::memcpy (data_, d, sizeof (data_));} + private: + #pragma db type(BINARY16_TYPE) column("") access(data) + char data_[16]; + }; + + #pragma db object + struct object4 + { + public: + const uuid& id () const {return id_;} + void id (const uuid& id) {id_ = id;} + private: + #pragma db id access(id) + uuid id_; + }; +} + +// Test version accessor/modifier functionality. +// +#pragma db namespace table("t6_") +namespace test6 +{ + #pragma db object optimistic + struct object1 + { + object1 (unsigned long id = 0): id_ (id), version_ (0) {} + + #pragma db id + unsigned long id_; + + public: + unsigned long version () const {return version_;} + void version (unsigned long version) {version_ = version;} + private: + #pragma db version access(version) + unsigned long version_; + }; + + #pragma db object optimistic + struct object2 + { + object2 (): version_ (0) {} + + #pragma db id auto + unsigned long id_; + + public: + unsigned long version () const {return version_;} + unsigned long& version () {return version_;} + private: + #pragma db version access(version) + unsigned long version_; + }; + + #pragma db object optimistic + struct object3 + { + object3 (unsigned long id = 0): id_ (id), version_ (0) {} + + #pragma db id + unsigned long id_; + + #pragma db version + const unsigned long version_; + }; +} + +// Test automatic discovery of accessor/modifier functions. +// +#pragma db namespace table("t7_") +namespace test7 +{ + #pragma db object + struct object + { + object () {} + object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + public: + int i1 () const {return i1_;} + int& i1 () {return i1_;} + private: + int i1_; + + public: + const int& get_i2 () const {return i2_;} + void set_i2 (int i2) {i2_ = i2;} + private: + int i2_; + + public: + const int& getI3 () const {return i3_;} + void setI3 (const int& i3) {i3_ = i3;} + private: + int i3_; + + public: + int geti4 () const {return i4;} + int seti4 (int v) {int r (i4); i4 = v; return r;} + private: + int i4; + + // Prefer reference modifier. + // + public: + int i5 () const {return i5_;} + int& i5 () {return i5_;} + void i5 (int i5); + private: + int i5_; + + // Prefer reference modifier (reverse function order). + // + public: + int i6 () const {return i6_;} + void i6 (int i6); + int& i6 () {return i6_;} + private: + int i6_; + + // Custom accessor/modifier regex. + // + public: + int GetI7 () const {return i7_;} + void SetI7 (int i7) {i7_ = i7;} + private: + int i7_; + + // Array member via ref. + // + public: + const char* b1 () const {return b1_;} + char* b1 () {return b1_;} + private: + #pragma db type(BINARY16_TYPE) + char b1_[16]; + + // Array member via modifier. + // + public: + const char* b2 () const {return b2_;} + void b2 (const char* b2) {std::memcpy (b2_, b2, sizeof (b2_));} + private: + #pragma db type(BINARY16_TYPE) + char b2_[16]; + + public: + bool operator== (const object& o) const + { + return id_ == o.id_ && + i1_ == o.i1_ && + i2_ == o.i2_ && + i3_ == o.i3_ && + i4 == o.i4 && + i5_ == o.i5_ && + i6_ == o.i6_ && + i7_ == o.i7_ && + std::memcmp (b1_, o.b1_, sizeof (b1_)) == 0 && + std::memcmp (b2_, o.b2_, sizeof (b2_)) == 0; + } + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/access/testscript b/odb-tests/common/access/testscript new file mode 100644 index 0000000..b0933f9 --- /dev/null +++ b/odb-tests/common/access/testscript @@ -0,0 +1,53 @@ +# file : common/access/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/as/buildfile b/odb-tests/common/as/buildfile new file mode 100644 index 0000000..dcdc961 --- /dev/null +++ b/odb-tests/common/as/buildfile @@ -0,0 +1,49 @@ +# file : common/as/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 as_ \ + --generate-schema \ + --generate-query \ + --generate-session + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# @@ BUILD@ Temporarily suppress the following warning: +# +# test-odb.cxx(6234): warning C4244: 'argument': conversion from 'id_type::value_type' to 'test5::version_type::value_type', possible loss of data +# +if ($cxx.class == 'msvc') + cxx.coptions += /wd4244 + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc diff --git a/odb-tests/common/as/driver.cxx b/odb-tests/common/as/driver.cxx new file mode 100644 index 0000000..578eb23 --- /dev/null +++ b/odb-tests/common/as/driver.cxx @@ -0,0 +1,348 @@ +// file : common/as/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test C++ type mapping (#pragma map type as ...). +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic type mapping functionality. + // + { + using namespace test1; + + object o1 (true, green, 123, 234); + o1.m[false] = 123; + o1.v.push_back (o1.ip); + o1.cv.push_back (red); + o1.cv.push_back (green); + + object o2 (false, blue, 234, 456); + o2.m[true] = 234; + o2.v.push_back (o2.ip); + o2.cv.push_back (green); + o2.cv.push_back (blue); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.b = false; + o1.c = blue; + o1.ip.first++; + o1.ip.second--; + o1.m[false]++; + o1.m[true] = 234; + o1.v.back () = o1.ip; + o1.cv.modify_front () = green; + o1.cv.push_back (red); + + o2.b = true; + o2.c = red; + o2.ip.first--; + o2.ip.second++; + o2.m[true]--; + o2.m[false] = 345; + o2.v.push_back (o2.ip); + o2.cv.pop_back (); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Test wrapped simple type mapping. + // + { + using namespace test2; + + object o1; + o1.v.push_back (null_bool ()); + o1.v.push_back (false); + + object o2; + o2.b = true; + o2.v.push_back (true); + o2.v.push_back (null_bool ()); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.b = false; + o1.v[0] = true; + o1.v[1].reset (); + + o2.b.reset (); + o2.v.push_back (false); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Test wrapped composite type mapping. + // + { + using namespace test3; + + object o1; + o1.ip = intp (0, 0); // NULL + o1.npv.push_back (o1.np); + o1.ipv.push_back (o1.ip); + + object o2; + o2.np = intp (123, 234); + o1.ip = intp (234, 123); + o2.npv.push_back (o2.np); + o2.ipv.push_back (o2.ip); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.np = o1.npv[0] = intp (234, 456); + o1.ip = o1.ipv.modify_at (0) = intp (456, 234); + + o2.np.reset (); + o2.npv[0].reset (); + o2.ip = o2.ipv.modify_at (0) = intp (0, 0); // NULL + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Test id type mapping. + // + { + using namespace test4; + + object o1 (123); + o1.v.push_back (1); + o1.v.push_back (2); + o1.v.push_back (3); + + object o2 (234); + o2.v.push_back (3); + o2.v.push_back (2); + o2.v.push_back (1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.i++; + o1.v.pop_back (); + o1.v.modify_front ()++; + + o2.i--; + o2.v.clear (); + o2.v.push_back (4); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Test version type mapping. + // + { + using namespace test5; + + object o1 (100, 123); + object o2 (200, 234); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + + assert (*p1 == o1); + assert (*p2 == o2); + + p1->i--; + p2->i++; + + db->update (*p1); + db->update (*p2); + + t.commit (); + } + + { + transaction t (db->begin ()); + + for (;;) + { + o1.i++; + o2.i--; + + try + { + + db->update (o1); + db->update (o2); + break; + } + catch (const odb::object_changed&) + { + db->reload (o1); + db->reload (o2); + } + } + + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/as/test.hxx b/odb-tests/common/as/test.hxx new file mode 100644 index 0000000..963abeb --- /dev/null +++ b/odb-tests/common/as/test.hxx @@ -0,0 +1,270 @@ +// file : common/as/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <map> +#include <vector> +#include <string> +#include <utility> // pair + +#include <odb/core.hxx> +#include <odb/vector.hxx> +#include <odb/nullable.hxx> + +// Test basic type mapping functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + enum color {red, green, blue}; + + inline const char* + color_to_string (color c) + { + return c == red ? "RED" : (c == green ? "GREEN" : "BLUE"); + } + + inline color + string_to_color (const std::string& s) + { + return s == "RED" ? red : (s == "GREEN" ? green : blue); + } + + #pragma db map type(color) as(std::string) \ + to(test1::color_to_string (?)) \ + from(test1::string_to_color (?)) + + typedef std::pair<int, int> intp; + + #pragma db value + struct comp + { + comp () {} + comp (int n1_, int n2_): n1 (n1_), n2 (n2_) {} + + int n1; + int n2; + }; + + #pragma db map type(intp) as(comp) \ + to(test1::comp ((?).first, (?).second)) \ + from(test1::intp ((?).n1, (?).n2)) + + #pragma db object + struct object + { + // Class-scope mapping. + // + #pragma db map type(bool) as(std::string) \ + to((?) ? "true" : "false") \ + from((?) == "true") + + #pragma db id auto + unsigned long id; + + bool b; + color c; + intp ip; + + std::map<bool, int> m; + std::vector<intp> v; + odb::vector<color> cv; + + object () {} + object (bool b_, color c_, int n1, int n2): b (b_), c (c_), ip (n1, n2) {} + }; + + inline bool + operator== (const object& x, const object y) + { + return + x.b == y.b && + x.c == y.c && + x.ip == y.ip && + x.m == y.m && + x.v == y.v && + x.cv == y.cv; + } +} + +// Test wrapped simple type mapping. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db map type(bool) as(std::string) \ + to((?) ? "true" : "false") \ + from((?) == "true") + + typedef odb::nullable<bool> null_bool; + typedef odb::nullable<std::string> null_string; + + /* + #pragma db map type(null_bool) as(null_string) \ + to((?) \ + ? test2::null_string (*(?) ? "true" : "false") \ + : test2::null_string ()) \ + from((?) \ + ? test2::null_bool (*(?) == "true") \ + : test2::null_bool ()) + */ + + #pragma db map type(null_bool) as(std::string) \ + to((?) ? (*(?) ? "true" : "false") : "null") \ + from((?) != "null" \ + ? test2::null_bool ((?) == "true") \ + : test2::null_bool ()) + + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + odb::nullable<bool> b; + std::vector<odb::nullable<bool> > v; + }; + + inline bool + operator== (const object& x, const object y) + { + return x.b == y.b && x.v == y.v; + } +} + +// Test wrapped simple type mapping. +// +#pragma db namespace table("t3_") +namespace test3 +{ + typedef std::pair<int, int> intp; + + #pragma db value + struct comp + { + comp () {} + comp (int n1_, int n2_): n1 (n1_), n2 (n2_) {} + + int n1; + int n2; + }; + + typedef odb::nullable<intp> null_intp; + typedef odb::nullable<comp> null_comp; + + #pragma db map type(null_intp) as(null_comp) \ + to((?) \ + ? test3::null_comp (test3::comp ((?)->first, (?)->second)) \ + : test3::null_comp ()) \ + from((?) \ + ? test3::null_intp (test3::intp ((?)->n1, (?)->n2)) \ + : test3::null_intp ()) + + // Map int pair with both members equal 0 to NULL comp. + // + #pragma db map type(intp) as(null_comp) \ + to((?).first != 0 || (?).second != 0 \ + ? test3::null_comp (test3::comp ((?).first, (?).second)) \ + : test3::null_comp ()) \ + from((?) \ + ? test3::intp (test3::intp ((?)->n1, (?)->n2)) \ + : test3::intp (0, 0)) + + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + odb::nullable<intp> np; + intp ip; + + std::vector<odb::nullable<intp> > npv; + odb::vector<intp> ipv; + }; + + inline bool + operator== (const object& x, const object y) + { + return x.np == y.np && x.ip == y.ip && x.npv == y.npv && x.ipv == y.ipv; + } +} + +// Test id type mapping. +// +struct id_type +{ + typedef unsigned long value_type; + value_type value; + + id_type (value_type v = 0): value (v) {} + operator value_type () const {return value;} +}; + +#pragma db map type(id_type) as(id_type::value_type) + +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db object + struct object + { + #pragma db id auto + id_type id; + + int i; + odb::vector<int> v; + + object () {} + object (int i_): i (i_) {} + }; + + inline bool + operator== (const object& x, const object y) + { + return x.id == y.id && x.i == y.i && x.v == y.v; + } +} + +// Test version type mapping. +// +#pragma db namespace table("t5_") +namespace test5 +{ + struct version_type + { + typedef unsigned short value_type; + value_type value; + + version_type (value_type v = 0): value (v) {} + operator value_type () const {return value;} + version_type& operator++ () {value++; return *this;} + }; + + #pragma db map type(version_type) as(id_type::value_type) + + #pragma db object optimistic + struct object + { + #pragma db id + id_type id; + + #pragma db version + version_type v; + + int i; + + object () {} + object (id_type id_, int i_): id (id_), i (i_) {} + }; + + inline bool + operator== (const object& x, const object y) + { + return x.id == y.id && x.v == y.v && x.i == y.i; + } +} + +#endif // TEST_HXX diff --git a/odb-tests/common/as/testscript b/odb-tests/common/as/testscript new file mode 100644 index 0000000..fc1d0d4 --- /dev/null +++ b/odb-tests/common/as/testscript @@ -0,0 +1,53 @@ +# file : common/as/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/blob/buildfile b/odb-tests/common/blob/buildfile new file mode 100644 index 0000000..cc6d164 --- /dev/null +++ b/odb-tests/common/blob/buildfile @@ -0,0 +1,40 @@ +# file : common/blob/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 blob_ \ + --generate-schema + +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/blob/driver.cxx b/odb-tests/common/blob/driver.cxx new file mode 100644 index 0000000..269f415 --- /dev/null +++ b/odb-tests/common/blob/driver.cxx @@ -0,0 +1,76 @@ +// file : common/blob/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test BLOB mapping. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + const char data[] = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B" + "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B" + "cccccccccccccccccccccccccccccccccccccccccccccccc" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B" + "dddddddddddddddddddddddddddddddddddddddddddddddd" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B" + "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B" + "ffffffffffffffffffffffffffffffffffffffffffffffff"; + + const unsigned char* udata = reinterpret_cast<const unsigned char*> (data); + + object o (1); + o.vc.assign (data, data + sizeof (data)); + o.vuc.assign (udata, udata + sizeof (data)); + memcpy (o.c, data, sizeof (data)); + memcpy (o.uc, udata, sizeof (data)); + memcpy (o.a.data (), data, sizeof (data)); + memcpy (o.ua.data (), udata, sizeof (data)); + o.cont.push_back (1); + o.cont.push_back (2); + o.cont.push_back (3); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + t.commit (); + + assert (o == *o1); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/blob/test.hxx b/odb-tests/common/blob/test.hxx new file mode 100644 index 0000000..9602ca2 --- /dev/null +++ b/odb-tests/common/blob/test.hxx @@ -0,0 +1,71 @@ +// file : common/blob/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <array> +#include <vector> +#include <cstring> // std::memcmp + +#include <odb/core.hxx> + +#ifdef ODB_COMPILER +# if defined(ODB_DATABASE_PGSQL) +# define BLOB_TYPE "BYTEA" +# elif defined(ODB_DATABASE_MSSQL) +//# define BLOB_TYPE "VARBINARY(1024)" +# define BLOB_TYPE "VARBINARY(max)" +# else +//# define BLOB_TYPE "RAW(1024)" +# define BLOB_TYPE "BLOB" +# endif +#endif + +#pragma db object +struct object +{ + object () {} + object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db type(BLOB_TYPE) + std::vector<char> vc; + + #pragma db type(BLOB_TYPE) + std::vector<unsigned char> vuc; + + #pragma db type(BLOB_TYPE) + char c[1024]; + + #pragma db type(BLOB_TYPE) + unsigned char uc[1024]; + + #pragma db type(BLOB_TYPE) + std::array<char, 1024> a; + + #pragma db type(BLOB_TYPE) + std::array<char, 1024> ua; + + // Make sure we can still use std::vector<char> and std::array<char> + // as containers. + // + std::vector<unsigned char> cont; +}; + +inline bool +operator== (const object& x, const object& y) +{ + return x.id_ == y.id_ + && x.vc == y.vc + && x.vuc == y.vuc + && std::memcmp (x.c, y.c, sizeof (x.c)) == 0 + && std::memcmp (x.uc, y.uc, sizeof (x.uc)) == 0 + && x.a == y.a + && x.ua == y.ua + && x.cont == y.cont; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/blob/testscript b/odb-tests/common/blob/testscript new file mode 100644 index 0000000..3b7f962 --- /dev/null +++ b/odb-tests/common/blob/testscript @@ -0,0 +1,53 @@ +# file : common/blob/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/buildfile b/odb-tests/common/buildfile new file mode 100644 index 0000000..cb9c748 --- /dev/null +++ b/odb-tests/common/buildfile @@ -0,0 +1,6 @@ +# file : common/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +./: {*/ -bulk/} + +./: bulk/: include = (!$pgsql || $pgsql_bulk || $size($databases) != 1) 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 +} diff --git a/odb-tests/common/callback/buildfile b/odb-tests/common/callback/buildfile new file mode 100644 index 0000000..2ecc3b8 --- /dev/null +++ b/odb-tests/common/callback/buildfile @@ -0,0 +1,41 @@ +# file : common/callback/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 callback_ \ + --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/callback/driver.cxx b/odb-tests/common/callback/driver.cxx new file mode 100644 index 0000000..80513c6 --- /dev/null +++ b/odb-tests/common/callback/driver.cxx @@ -0,0 +1,184 @@ +// file : common/callback/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test database operation callbacks. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +const char* events[] = +{ + "pre_persist", + "post_persist", + "pre_load", + "post_load", + "pre_update", + "post_update", + "pre_erase", + "post_erase" +}; + +void object:: +db_callback (callback_event e, database& db) +{ + cout << " " << events[e] << " " << id_ << endl; + + // Test custom recursive loading. + // + if (e == callback_event::post_load && ref != 0) + { + robj = db.load<object> (ref); + cout << " " << id_ << ' ' << ref << ' ' << robj->id_ << endl; + } +} + +void object:: +db_callback (callback_event e, database&) const +{ + cout << " " << events[e] << " " << id_ << " const" << endl; +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Persist. + // + cout << "persist" << endl; + { + object o1 (1, 1); + object const o2 (2, 2); + transaction t (db->begin ()); + db->persist (o1); + db->persist (&o2); + t.commit (); + } + cout << "***" << endl; + + // Load. + // + cout << "load" << endl; + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + object o2; + db->load<object> (2, o2); + t.commit (); + } + cout << "***" << endl; + + // Query. + // + cout << "query" << endl; + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + + result r (db->query<object> ((query::id < 3) + "ORDER BY" + query::id)); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + if (i->id_ > 3) // Load. + break; + } + + t.commit (); + } + cout << "***" << endl; + + // Update. + // + cout << "update" << endl; + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + unique_ptr<object> o2 (db->load<object> (2)); + o1->data++; + o2->data++; + db->update (o1.get ()); + db->update (static_cast<const object&> (*o2)); + t.commit (); + } + cout << "***" << endl; + + // Erase. + // + cout << "erase" << endl; + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + unique_ptr<object> o2 (db->load<object> (2)); + db->erase (static_cast<const object*> (o1.get ())); + db->erase (*o2); + t.commit (); + } + cout << "***" << endl; + + // Delayed (recursive) load. + // + cout << "delayed load" << endl; + { + { + object o1 (1, 1); + object o2 (2, 2); + object o3 (3, 3); + object o4 (4, 4); + + o1.pobj = &o2; + o1.ref = 4; + + o2.pobj = &o3; + o2.ref = 4; + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + db->persist (o4); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + object* o2 (o1->pobj); + + cout << o1->id_ << ' ' << o1->ref << ' ' << o1->robj->id_ << endl; + cout << o2->id_ << ' ' << o2->ref << ' ' << o2->robj->id_ << endl; + + delete o1->robj; + delete o2->robj; + + delete o2->pobj; + delete o2; + t.commit (); + } + } + cout << "***" << endl; + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/callback/test.hxx b/odb-tests/common/callback/test.hxx new file mode 100644 index 0000000..bd30907 --- /dev/null +++ b/odb-tests/common/callback/test.hxx @@ -0,0 +1,43 @@ +// file : common/callback/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <odb/core.hxx> +#include <odb/callback.hxx> + +#pragma db object callback(db_callback) +struct object +{ + object (unsigned long id, unsigned long d) + : id_ (id), data (d), pobj (0), robj (0), ref (0) + { + } + + object () + : id_ (0), pobj (0), robj (0) + { + } + + #pragma db id + unsigned long id_; + + unsigned long data; + + object* pobj; + + // Test custom recursive loading. + // + #pragma db transient + object* robj; + unsigned long ref; // Unless 0, reference to another object. + + void + db_callback (odb::callback_event, odb::database&); + + void + db_callback (odb::callback_event, odb::database&) const; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/callback/testscript b/odb-tests/common/callback/testscript new file mode 100644 index 0000000..30a4336 --- /dev/null +++ b/odb-tests/common/callback/testscript @@ -0,0 +1,120 @@ +# file : common/callback/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + ++cat <<EOI >=output + persist + pre_persist 1 const + post_persist 1 const + pre_persist 2 const + post_persist 2 const + *** + load + pre_load 0 + post_load 1 + pre_load 0 + post_load 2 + *** + query + pre_load 0 + post_load 1 + pre_load 0 + post_load 2 + *** + update + pre_load 0 + post_load 1 + pre_load 0 + post_load 2 + pre_update 1 const + post_update 1 const + pre_update 2 const + post_update 2 const + *** + erase + pre_load 0 + post_load 1 + pre_load 0 + post_load 2 + pre_erase 1 const + post_erase 1 const + pre_erase 2 const + post_erase 2 const + *** + delayed load + pre_persist 1 const + post_persist 1 const + pre_persist 2 const + post_persist 2 const + pre_persist 3 const + post_persist 3 const + pre_persist 4 const + post_persist 4 const + pre_load 0 + pre_load 0 + pre_load 0 + post_load 3 + post_load 2 + pre_load 0 + post_load 4 + 2 4 4 + post_load 1 + pre_load 0 + post_load 4 + 1 4 4 + 1 4 4 + 2 4 4 + *** + EOI + +test.redirects += >>>../output + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/changelog/.gitignore b/odb-tests/common/changelog/.gitignore new file mode 100644 index 0000000..5352a2b --- /dev/null +++ b/odb-tests/common/changelog/.gitignore @@ -0,0 +1,3 @@ +# Generate ODB options file. +# +odb.options diff --git a/odb-tests/common/changelog/add-column-mssql-diff.xml b/odb-tests/common/changelog/add-column-mssql-diff.xml new file mode 100644 index 0000000..4f9ba09 --- /dev/null +++ b/odb-tests/common/changelog/add-column-mssql-diff.xml @@ -0,0 +1,16 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="num" type="INT" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-mssql-patch.xml b/odb-tests/common/changelog/add-column-mssql-patch.xml new file mode 100644 index 0000000..4f396d9 --- /dev/null +++ b/odb-tests/common/changelog/add-column-mssql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-mysql-diff.xml b/odb-tests/common/changelog/add-column-mysql-diff.xml new file mode 100644 index 0000000..992306d --- /dev/null +++ b/odb-tests/common/changelog/add-column-mysql-diff.xml @@ -0,0 +1,16 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="num" type="INT" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-mysql-patch.xml b/odb-tests/common/changelog/add-column-mysql-patch.xml new file mode 100644 index 0000000..14f3f01 --- /dev/null +++ b/odb-tests/common/changelog/add-column-mysql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-oracle-diff.xml b/odb-tests/common/changelog/add-column-oracle-diff.xml new file mode 100644 index 0000000..fa1dac6 --- /dev/null +++ b/odb-tests/common/changelog/add-column-oracle-diff.xml @@ -0,0 +1,16 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="num" type="NUMBER(10)" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-oracle-patch.xml b/odb-tests/common/changelog/add-column-oracle-patch.xml new file mode 100644 index 0000000..38fb8d6 --- /dev/null +++ b/odb-tests/common/changelog/add-column-oracle-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-pgsql-diff.xml b/odb-tests/common/changelog/add-column-pgsql-diff.xml new file mode 100644 index 0000000..9524d9d --- /dev/null +++ b/odb-tests/common/changelog/add-column-pgsql-diff.xml @@ -0,0 +1,16 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="num" type="INTEGER" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-pgsql-patch.xml b/odb-tests/common/changelog/add-column-pgsql-patch.xml new file mode 100644 index 0000000..7f7d9a0 --- /dev/null +++ b/odb-tests/common/changelog/add-column-pgsql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-sqlite-diff.xml b/odb-tests/common/changelog/add-column-sqlite-diff.xml new file mode 100644 index 0000000..b59cc72 --- /dev/null +++ b/odb-tests/common/changelog/add-column-sqlite-diff.xml @@ -0,0 +1,16 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="num" type="INTEGER" null="false"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column-sqlite-patch.xml b/odb-tests/common/changelog/add-column-sqlite-patch.xml new file mode 100644 index 0000000..fbe4428 --- /dev/null +++ b/odb-tests/common/changelog/add-column-sqlite-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-column.hxx b/odb-tests/common/changelog/add-column.hxx new file mode 100644 index 0000000..54eab42 --- /dev/null +++ b/odb-tests/common/changelog/add-column.hxx @@ -0,0 +1,20 @@ +// file : common/changelog/add-column.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ADD_COLUMN_HXX +#define ADD_COLUMN_HXX + +#pragma db model version(BVER, CVER, open) + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER > 1 + int num; +#endif +}; + +#endif // ADD_COLUMN_HXX diff --git a/odb-tests/common/changelog/add-foreign-key-diff.xml b/odb-tests/common/changelog/add-foreign-key-diff.xml new file mode 100644 index 0000000..d4f29ad --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="INTEGER" null="true"/> + <add-foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml b/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml new file mode 100644 index 0000000..76ebce3 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-mssql-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="INT" null="true"/> + <add-foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml b/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml new file mode 100644 index 0000000..6d75709 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-mssql-patch.xml @@ -0,0 +1,25 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml b/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml new file mode 100644 index 0000000..acdfd5b --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-mysql-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="INT" null="true"/> + <add-foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml b/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml new file mode 100644 index 0000000..adc9081 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-mysql-patch.xml @@ -0,0 +1,25 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml b/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml new file mode 100644 index 0000000..6bd8bc3 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-oracle-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="NUMBER(10)" null="true"/> + <add-foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml b/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml new file mode 100644 index 0000000..7aa01ea --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-oracle-patch.xml @@ -0,0 +1,25 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="o1" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml b/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml new file mode 100644 index 0000000..793b009 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-pgsql-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="INTEGER" null="true"/> + <add-foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml b/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml new file mode 100644 index 0000000..a256831 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-pgsql-patch.xml @@ -0,0 +1,25 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml b/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml new file mode 100644 index 0000000..1510470 --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-sqlite-diff.xml @@ -0,0 +1,28 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="o1" type="INTEGER" null="true"/> + <add-foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </add-foreign-key> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml b/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml new file mode 100644 index 0000000..1c2d0ea --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key-sqlite-patch.xml @@ -0,0 +1,25 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-foreign-key.hxx b/odb-tests/common/changelog/add-foreign-key.hxx new file mode 100644 index 0000000..2a43eea --- /dev/null +++ b/odb-tests/common/changelog/add-foreign-key.hxx @@ -0,0 +1,29 @@ +// file : common/changelog/add-foreign-key.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ADD_FOREIGN_KEY_HXX +#define ADD_FOREIGN_KEY_HXX + +#pragma db model version(BVER, CVER, open) + +struct object1; + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER > 1 + object1* o1; +#endif +}; + +#pragma db object +struct object1 +{ + #pragma db id + int id; +}; + +#endif // ADD_FOREIGN_KEY_HXX diff --git a/odb-tests/common/changelog/add-index-mssql-diff.xml b/odb-tests/common/changelog/add-index-mssql-diff.xml new file mode 100644 index 0000000..58c623f --- /dev/null +++ b/odb-tests/common/changelog/add-index-mssql-diff.xml @@ -0,0 +1,21 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="y" type="INT" null="false"/> + <add-index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </add-index> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="x" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-mssql-patch.xml b/odb-tests/common/changelog/add-index-mssql-patch.xml new file mode 100644 index 0000000..2b10e65 --- /dev/null +++ b/odb-tests/common/changelog/add-index-mssql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="x" type="INT" null="false"/> + <column name="y" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-mysql-diff.xml b/odb-tests/common/changelog/add-index-mysql-diff.xml new file mode 100644 index 0000000..a54a7e3 --- /dev/null +++ b/odb-tests/common/changelog/add-index-mysql-diff.xml @@ -0,0 +1,21 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="y" type="INT" null="false"/> + <add-index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </add-index> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="x" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-mysql-patch.xml b/odb-tests/common/changelog/add-index-mysql-patch.xml new file mode 100644 index 0000000..a2454b8 --- /dev/null +++ b/odb-tests/common/changelog/add-index-mysql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="x" type="INT" null="false"/> + <column name="y" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-oracle-diff.xml b/odb-tests/common/changelog/add-index-oracle-diff.xml new file mode 100644 index 0000000..80f8ecc --- /dev/null +++ b/odb-tests/common/changelog/add-index-oracle-diff.xml @@ -0,0 +1,21 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="y" type="NUMBER(10)" null="false"/> + <add-index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </add-index> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="x" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-oracle-patch.xml b/odb-tests/common/changelog/add-index-oracle-patch.xml new file mode 100644 index 0000000..a9bafea --- /dev/null +++ b/odb-tests/common/changelog/add-index-oracle-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="x" type="NUMBER(10)" null="false"/> + <column name="y" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-pgsql-diff.xml b/odb-tests/common/changelog/add-index-pgsql-diff.xml new file mode 100644 index 0000000..3988643 --- /dev/null +++ b/odb-tests/common/changelog/add-index-pgsql-diff.xml @@ -0,0 +1,21 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="y" type="INTEGER" null="false"/> + <add-index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </add-index> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="x" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-pgsql-patch.xml b/odb-tests/common/changelog/add-index-pgsql-patch.xml new file mode 100644 index 0000000..e9c564c --- /dev/null +++ b/odb-tests/common/changelog/add-index-pgsql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="x" type="INTEGER" null="false"/> + <column name="y" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-sqlite-diff.xml b/odb-tests/common/changelog/add-index-sqlite-diff.xml new file mode 100644 index 0000000..c1f7fdc --- /dev/null +++ b/odb-tests/common/changelog/add-index-sqlite-diff.xml @@ -0,0 +1,21 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <add-column name="y" type="INTEGER" null="false"/> + <add-index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </add-index> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="x" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index-sqlite-patch.xml b/odb-tests/common/changelog/add-index-sqlite-patch.xml new file mode 100644 index 0000000..b9512e0 --- /dev/null +++ b/odb-tests/common/changelog/add-index-sqlite-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="x" type="INTEGER" null="false"/> + <column name="y" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="xy_i" type="UNIQUE"> + <column name="x"/> + <column name="y" options="DESC"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-index.hxx b/odb-tests/common/changelog/add-index.hxx new file mode 100644 index 0000000..645cee2 --- /dev/null +++ b/odb-tests/common/changelog/add-index.hxx @@ -0,0 +1,24 @@ +// file : common/changelog/add-index.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ADD_INDEX_HXX +#define ADD_INDEX_HXX + +#pragma db model version(BVER, CVER, open) + +#pragma db object +struct object +{ + #pragma db id auto + int id; + + int x; + +#if CVER > 1 + int y; + #pragma db index ("xy_i") unique member(x) member(y, "DESC") +#endif + +}; + +#endif // ADD_INDEX_HXX diff --git a/odb-tests/common/changelog/add-table-mssql-diff.xml b/odb-tests/common/changelog/add-table-mssql-diff.xml new file mode 100644 index 0000000..8bf0e30 --- /dev/null +++ b/odb-tests/common/changelog/add-table-mssql-diff.xml @@ -0,0 +1,45 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <add-table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </add-table> + <add-table name="object_nums" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </add-table> + </changeset> + + <model version="1"> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-mssql-patch.xml b/odb-tests/common/changelog/add-table-mssql-patch.xml new file mode 100644 index 0000000..9dd41f3 --- /dev/null +++ b/odb-tests/common/changelog/add-table-mssql-patch.xml @@ -0,0 +1,44 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-mysql-diff.xml b/odb-tests/common/changelog/add-table-mysql-diff.xml new file mode 100644 index 0000000..57f741b --- /dev/null +++ b/odb-tests/common/changelog/add-table-mysql-diff.xml @@ -0,0 +1,45 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <add-table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </add-table> + <add-table name="object_nums" options="ENGINE=InnoDB" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT UNSIGNED" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </add-table> + </changeset> + + <model version="1"> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-mysql-patch.xml b/odb-tests/common/changelog/add-table-mysql-patch.xml new file mode 100644 index 0000000..0db1e9f --- /dev/null +++ b/odb-tests/common/changelog/add-table-mysql-patch.xml @@ -0,0 +1,44 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" options="ENGINE=InnoDB" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT UNSIGNED" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-oracle-diff.xml b/odb-tests/common/changelog/add-table-oracle-diff.xml new file mode 100644 index 0000000..70ec7c6 --- /dev/null +++ b/odb-tests/common/changelog/add-table-oracle-diff.xml @@ -0,0 +1,45 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <add-table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <column name="o1" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </add-table> + <add-table name="object_nums" kind="container"> + <column name="object_id" type="NUMBER(10)" null="false"/> + <column name="index" type="NUMBER(20)" null="false"/> + <column name="value" type="NUMBER(10)" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </add-table> + </changeset> + + <model version="1"> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-oracle-patch.xml b/odb-tests/common/changelog/add-table-oracle-patch.xml new file mode 100644 index 0000000..969c2e0 --- /dev/null +++ b/odb-tests/common/changelog/add-table-oracle-patch.xml @@ -0,0 +1,44 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <column name="o1" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="NUMBER(10)" null="false"/> + <column name="index" type="NUMBER(20)" null="false"/> + <column name="value" type="NUMBER(10)" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-pgsql-diff.xml b/odb-tests/common/changelog/add-table-pgsql-diff.xml new file mode 100644 index 0000000..9b48062 --- /dev/null +++ b/odb-tests/common/changelog/add-table-pgsql-diff.xml @@ -0,0 +1,45 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <add-table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </add-table> + <add-table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </add-table> + </changeset> + + <model version="1"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-pgsql-patch.xml b/odb-tests/common/changelog/add-table-pgsql-patch.xml new file mode 100644 index 0000000..b04a933 --- /dev/null +++ b/odb-tests/common/changelog/add-table-pgsql-patch.xml @@ -0,0 +1,44 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-sqlite-diff.xml b/odb-tests/common/changelog/add-table-sqlite-diff.xml new file mode 100644 index 0000000..573bc69 --- /dev/null +++ b/odb-tests/common/changelog/add-table-sqlite-diff.xml @@ -0,0 +1,45 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <add-table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </add-table> + <add-table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="INTEGER" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </add-table> + </changeset> + + <model version="1"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table-sqlite-patch.xml b/odb-tests/common/changelog/add-table-sqlite-patch.xml new file mode 100644 index 0000000..3506410 --- /dev/null +++ b/odb-tests/common/changelog/add-table-sqlite-patch.xml @@ -0,0 +1,44 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="INTEGER" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/add-table.hxx b/odb-tests/common/changelog/add-table.hxx new file mode 100644 index 0000000..a22e206 --- /dev/null +++ b/odb-tests/common/changelog/add-table.hxx @@ -0,0 +1,34 @@ +// file : common/changelog/add-table.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ADD_TABLE_HXX +#define ADD_TABLE_HXX + +#include <vector> + +#pragma db model version(BVER, CVER, open) + +struct object1; + +#if CVER > 1 +#pragma db object +struct object +{ + #pragma db id auto + int id; + int num; + + std::vector<int> nums; + object1* o1; +}; +#endif + +#pragma db object +struct object1 +{ + #pragma db id + int id; + int num; +}; + +#endif // ADD_TABLE_HXX diff --git a/odb-tests/common/changelog/alter-column-mssql-diff.xml b/odb-tests/common/changelog/alter-column-mssql-diff.xml new file mode 100644 index 0000000..6c1fb6a --- /dev/null +++ b/odb-tests/common/changelog/alter-column-mssql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <alter-column name="num" null="true"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-mssql-patch.xml b/odb-tests/common/changelog/alter-column-mssql-patch.xml new file mode 100644 index 0000000..15db347 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-mssql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-mysql-diff.xml b/odb-tests/common/changelog/alter-column-mysql-diff.xml new file mode 100644 index 0000000..39ad6ef --- /dev/null +++ b/odb-tests/common/changelog/alter-column-mysql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <alter-column name="num" null="true"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-mysql-patch.xml b/odb-tests/common/changelog/alter-column-mysql-patch.xml new file mode 100644 index 0000000..0131466 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-mysql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-oracle-diff.xml b/odb-tests/common/changelog/alter-column-oracle-diff.xml new file mode 100644 index 0000000..d41d333 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-oracle-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <alter-column name="num" null="true"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-oracle-patch.xml b/odb-tests/common/changelog/alter-column-oracle-patch.xml new file mode 100644 index 0000000..0e0794d --- /dev/null +++ b/odb-tests/common/changelog/alter-column-oracle-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-pgsql-diff.xml b/odb-tests/common/changelog/alter-column-pgsql-diff.xml new file mode 100644 index 0000000..fd97fa0 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-pgsql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <alter-column name="num" null="true"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-pgsql-patch.xml b/odb-tests/common/changelog/alter-column-pgsql-patch.xml new file mode 100644 index 0000000..dade1a3 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-pgsql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-sqlite-diff.xml b/odb-tests/common/changelog/alter-column-sqlite-diff.xml new file mode 100644 index 0000000..7ecea06 --- /dev/null +++ b/odb-tests/common/changelog/alter-column-sqlite-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <alter-column name="num" null="true"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column-sqlite-patch.xml b/odb-tests/common/changelog/alter-column-sqlite-patch.xml new file mode 100644 index 0000000..de2762e --- /dev/null +++ b/odb-tests/common/changelog/alter-column-sqlite-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/alter-column.hxx b/odb-tests/common/changelog/alter-column.hxx new file mode 100644 index 0000000..02f091d --- /dev/null +++ b/odb-tests/common/changelog/alter-column.hxx @@ -0,0 +1,21 @@ +// file : common/changelog/alter-column.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef ALTER_COLUMN_HXX +#define ALTER_COLUMN_HXX + +#pragma db model version(BVER, CVER, open) + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER > 1 + #pragma db null +#endif + int num; +}; + +#endif // ALTER_COLUMN_HXX diff --git a/odb-tests/common/changelog/buildfile b/odb-tests/common/changelog/buildfile new file mode 100644 index 0000000..751b7a7 --- /dev/null +++ b/odb-tests/common/changelog/buildfile @@ -0,0 +1,34 @@ +# file : common/changelog/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +./: file{odb.options} xml{*} testscript + +# Introduce the metadata library target to make sure the libodb library is +# resolved for the below ad hoc recipe. +# +libue{test-meta}: $libodb + +file{odb.options}: libue{test-meta} +{{ + pops = $cxx.lib_poptions($<[0]) + depdb hash $pops + + diag poptions ($<[0]) -> $> + + f = $path($>[0]) + rm -f $f + + touch $f + + for o: $pops + echo $o >+$f + end +}} + +# Testscript's run-time prerequisites. +# +testscript@./: test = $odb + +./: $odb: clean = false diff --git a/odb-tests/common/changelog/drop-column-mssql-diff.xml b/odb-tests/common/changelog/drop-column-mssql-diff.xml new file mode 100644 index 0000000..f2bab96 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-mssql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-column name="num"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-mssql-patch.xml b/odb-tests/common/changelog/drop-column-mssql-patch.xml new file mode 100644 index 0000000..32402a3 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-mssql-patch.xml @@ -0,0 +1,12 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-mysql-diff.xml b/odb-tests/common/changelog/drop-column-mysql-diff.xml new file mode 100644 index 0000000..2bb321d --- /dev/null +++ b/odb-tests/common/changelog/drop-column-mysql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-column name="num"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-mysql-patch.xml b/odb-tests/common/changelog/drop-column-mysql-patch.xml new file mode 100644 index 0000000..6572ebe --- /dev/null +++ b/odb-tests/common/changelog/drop-column-mysql-patch.xml @@ -0,0 +1,12 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-oracle-diff.xml b/odb-tests/common/changelog/drop-column-oracle-diff.xml new file mode 100644 index 0000000..e920a12 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-oracle-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-column name="num"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-oracle-patch.xml b/odb-tests/common/changelog/drop-column-oracle-patch.xml new file mode 100644 index 0000000..b113664 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-oracle-patch.xml @@ -0,0 +1,12 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-pgsql-diff.xml b/odb-tests/common/changelog/drop-column-pgsql-diff.xml new file mode 100644 index 0000000..d2e91ba --- /dev/null +++ b/odb-tests/common/changelog/drop-column-pgsql-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-column name="num"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-pgsql-patch.xml b/odb-tests/common/changelog/drop-column-pgsql-patch.xml new file mode 100644 index 0000000..06cc73d --- /dev/null +++ b/odb-tests/common/changelog/drop-column-pgsql-patch.xml @@ -0,0 +1,12 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-sqlite-diff.xml b/odb-tests/common/changelog/drop-column-sqlite-diff.xml new file mode 100644 index 0000000..1c9d138 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-sqlite-diff.xml @@ -0,0 +1,17 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-column name="num"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column-sqlite-patch.xml b/odb-tests/common/changelog/drop-column-sqlite-patch.xml new file mode 100644 index 0000000..6887530 --- /dev/null +++ b/odb-tests/common/changelog/drop-column-sqlite-patch.xml @@ -0,0 +1,12 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-column.hxx b/odb-tests/common/changelog/drop-column.hxx new file mode 100644 index 0000000..3de237d --- /dev/null +++ b/odb-tests/common/changelog/drop-column.hxx @@ -0,0 +1,20 @@ +// file : common/changelog/drop-column.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef DROP_COLUMN_HXX +#define DROP_COLUMN_HXX + +#pragma db model version(BVER, CVER, open) + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER == 1 + int num; +#endif +}; + +#endif // DROP_COLUMN_HXX diff --git a/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml new file mode 100644 index 0000000..da3ce73 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-mssql-diff.xml @@ -0,0 +1,30 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-foreign-key name="object_o1_fk"/> + <drop-column name="o1"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml new file mode 100644 index 0000000..cd1a372 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-mssql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml new file mode 100644 index 0000000..aa179f2 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-mysql-diff.xml @@ -0,0 +1,30 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-foreign-key name="object_o1_fk"/> + <drop-column name="o1"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml new file mode 100644 index 0000000..67f026e --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-mysql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml b/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml new file mode 100644 index 0000000..aa407d3 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-oracle-diff.xml @@ -0,0 +1,30 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-foreign-key name="object_o1_fk"/> + <drop-column name="o1"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="o1" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml b/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml new file mode 100644 index 0000000..56253f0 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-oracle-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml b/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml new file mode 100644 index 0000000..9a6259a --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-pgsql-diff.xml @@ -0,0 +1,30 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-foreign-key name="o1_fk"/> + <drop-column name="o1"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml b/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml new file mode 100644 index 0000000..df024b4 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-pgsql-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml b/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml new file mode 100644 index 0000000..6f9f994 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-sqlite-diff.xml @@ -0,0 +1,30 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-foreign-key name="o1_fk"/> + <drop-column name="o1"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml b/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml new file mode 100644 index 0000000..6e63218 --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key-sqlite-patch.xml @@ -0,0 +1,18 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-foreign-key.hxx b/odb-tests/common/changelog/drop-foreign-key.hxx new file mode 100644 index 0000000..ba3005f --- /dev/null +++ b/odb-tests/common/changelog/drop-foreign-key.hxx @@ -0,0 +1,29 @@ +// file : common/changelog/drop-foreign-key.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef DROP_FOREIGN_KEY_HXX +#define DROP_FOREIGN_KEY_HXX + +#pragma db model version(BVER, CVER, open) + +struct object1; + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER == 1 + object1* o1; +#endif +}; + +#pragma db object +struct object1 +{ + #pragma db id + int id; +}; + +#endif // DROP_FOREIGN_KEY_HXX diff --git a/odb-tests/common/changelog/drop-index-mssql-diff.xml b/odb-tests/common/changelog/drop-index-mssql-diff.xml new file mode 100644 index 0000000..ac95db5 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-mssql-diff.xml @@ -0,0 +1,20 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-index name="num_i"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="num_i"> + <column name="num"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-mssql-patch.xml b/odb-tests/common/changelog/drop-index-mssql-patch.xml new file mode 100644 index 0000000..4f396d9 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-mssql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-mysql-diff.xml b/odb-tests/common/changelog/drop-index-mysql-diff.xml new file mode 100644 index 0000000..f8c95ef --- /dev/null +++ b/odb-tests/common/changelog/drop-index-mysql-diff.xml @@ -0,0 +1,20 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-index name="num_i"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="num_i"> + <column name="num"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-mysql-patch.xml b/odb-tests/common/changelog/drop-index-mysql-patch.xml new file mode 100644 index 0000000..14f3f01 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-mysql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-oracle-diff.xml b/odb-tests/common/changelog/drop-index-oracle-diff.xml new file mode 100644 index 0000000..d174802 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-oracle-diff.xml @@ -0,0 +1,20 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-index name="object_num_i"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <index name="object_num_i"> + <column name="num"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-oracle-patch.xml b/odb-tests/common/changelog/drop-index-oracle-patch.xml new file mode 100644 index 0000000..38fb8d6 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-oracle-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-pgsql-diff.xml b/odb-tests/common/changelog/drop-index-pgsql-diff.xml new file mode 100644 index 0000000..375a3d8 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-pgsql-diff.xml @@ -0,0 +1,20 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-index name="object_num_i"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="object_num_i"> + <column name="num"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-pgsql-patch.xml b/odb-tests/common/changelog/drop-index-pgsql-patch.xml new file mode 100644 index 0000000..7f7d9a0 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-pgsql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-sqlite-diff.xml b/odb-tests/common/changelog/drop-index-sqlite-diff.xml new file mode 100644 index 0000000..bf54f9d --- /dev/null +++ b/odb-tests/common/changelog/drop-index-sqlite-diff.xml @@ -0,0 +1,20 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <alter-table name="object"> + <drop-index name="object_num_i"/> + </alter-table> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <index name="object_num_i"> + <column name="num"/> + </index> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index-sqlite-patch.xml b/odb-tests/common/changelog/drop-index-sqlite-patch.xml new file mode 100644 index 0000000..fbe4428 --- /dev/null +++ b/odb-tests/common/changelog/drop-index-sqlite-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-index.hxx b/odb-tests/common/changelog/drop-index.hxx new file mode 100644 index 0000000..08fecba --- /dev/null +++ b/odb-tests/common/changelog/drop-index.hxx @@ -0,0 +1,21 @@ +// file : common/changelog/drop-index.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef DROP_INDEX_HXX +#define DROP_INDEX_HXX + +#pragma db model version(BVER, CVER, open) + +#pragma db object +struct object +{ + #pragma db id auto + int id; + +#if CVER == 1 + #pragma db index +#endif + int num; +}; + +#endif // DROP_INDEX_HXX diff --git a/odb-tests/common/changelog/drop-table-mssql-diff.xml b/odb-tests/common/changelog/drop-table-mssql-diff.xml new file mode 100644 index 0000000..399d8bc --- /dev/null +++ b/odb-tests/common/changelog/drop-table-mssql-diff.xml @@ -0,0 +1,47 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="2"> + <drop-table name="object"/> + <drop-table name="object_nums"/> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-mssql-patch.xml b/odb-tests/common/changelog/drop-table-mssql-patch.xml new file mode 100644 index 0000000..c5dda75 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-mssql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-mysql-diff.xml b/odb-tests/common/changelog/drop-table-mysql-diff.xml new file mode 100644 index 0000000..3ccd553 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-mysql-diff.xml @@ -0,0 +1,47 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="2"> + <drop-table name="object"/> + <drop-table name="object_nums"/> + </changeset> + + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <column name="o1" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" options="ENGINE=InnoDB" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT UNSIGNED" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-mysql-patch.xml b/odb-tests/common/changelog/drop-table-mysql-patch.xml new file mode 100644 index 0000000..250bd20 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-mysql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-oracle-diff.xml b/odb-tests/common/changelog/drop-table-oracle-diff.xml new file mode 100644 index 0000000..589d64b --- /dev/null +++ b/odb-tests/common/changelog/drop-table-oracle-diff.xml @@ -0,0 +1,47 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="2"> + <drop-table name="object"/> + <drop-table name="object_nums"/> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <column name="o1" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="NUMBER(10)" null="false"/> + <column name="index" type="NUMBER(20)" null="false"/> + <column name="value" type="NUMBER(10)" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-oracle-patch.xml b/odb-tests/common/changelog/drop-table-oracle-patch.xml new file mode 100644 index 0000000..5f222dc --- /dev/null +++ b/odb-tests/common/changelog/drop-table-oracle-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-pgsql-diff.xml b/odb-tests/common/changelog/drop-table-pgsql-diff.xml new file mode 100644 index 0000000..168ec46 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-pgsql-diff.xml @@ -0,0 +1,47 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="2"> + <drop-table name="object"/> + <drop-table name="object_nums"/> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-pgsql-patch.xml b/odb-tests/common/changelog/drop-table-pgsql-patch.xml new file mode 100644 index 0000000..ebb7000 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-pgsql-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-sqlite-diff.xml b/odb-tests/common/changelog/drop-table-sqlite-diff.xml new file mode 100644 index 0000000..6a258c4 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-sqlite-diff.xml @@ -0,0 +1,47 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="2"> + <drop-table name="object"/> + <drop-table name="object_nums"/> + </changeset> + + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <column name="o1" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1"/> + <references table="object1"> + <column name="id"/> + </references> + </foreign-key> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="INTEGER" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table-sqlite-patch.xml b/odb-tests/common/changelog/drop-table-sqlite-patch.xml new file mode 100644 index 0000000..45c6f00 --- /dev/null +++ b/odb-tests/common/changelog/drop-table-sqlite-patch.xml @@ -0,0 +1,13 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <changeset version="3"/> + + <model version="2"> + <table name="object1" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/drop-table.hxx b/odb-tests/common/changelog/drop-table.hxx new file mode 100644 index 0000000..2919e52 --- /dev/null +++ b/odb-tests/common/changelog/drop-table.hxx @@ -0,0 +1,34 @@ +// file : common/changelog/drop-table.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef DROP_TABLE_HXX +#define DROP_TABLE_HXX + +#include <vector> + +#pragma db model version(BVER, CVER, open) + +struct object1; + +#if CVER == 1 +#pragma db object +struct object +{ + #pragma db id auto + int id; + int num; + + std::vector<int> nums; + object1* o1; +}; +#endif + +#pragma db object +struct object1 +{ + #pragma db id + int id; + int num; +}; + +#endif // DROP_TABLE_HXX diff --git a/odb-tests/common/changelog/model-mssql-diff.xml b/odb-tests/common/changelog/model-mssql-diff.xml new file mode 120000 index 0000000..e1f812d --- /dev/null +++ b/odb-tests/common/changelog/model-mssql-diff.xml @@ -0,0 +1 @@ +model-mssql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-mssql-patch.xml b/odb-tests/common/changelog/model-mssql-patch.xml new file mode 120000 index 0000000..e1f812d --- /dev/null +++ b/odb-tests/common/changelog/model-mssql-patch.xml @@ -0,0 +1 @@ +model-mssql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-mssql.xml b/odb-tests/common/changelog/model-mssql.xml new file mode 100644 index 0000000..509a210 --- /dev/null +++ b/odb-tests/common/changelog/model-mssql.xml @@ -0,0 +1,56 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mssql" version="1"> + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="true" default="0" options="DUMMY=1"/> + <column name="v_x" type="INT" null="false"/> + <column name="v_y" type="INT" null="false"/> + <column name="o1_x" type="INT" null="true"/> + <column name="o1_y" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1_x"/> + <column name="o1_y"/> + <references table="object1"> + <column name="id_x"/> + <column name="id_y"/> + </references> + </foreign-key> + <index name="num_i" type="UNIQUE" method="BTREE" options="DUMMY=1"> + <column name="num" options="DESC"/> + </index> + <index name="v_i"> + <column name="v_x"/> + <column name="v_y"/> + </index> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id_x" type="INT" null="false"/> + <column name="id_y" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id_x"/> + <column name="id_y"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/model-mysql-diff.xml b/odb-tests/common/changelog/model-mysql-diff.xml new file mode 120000 index 0000000..9100280 --- /dev/null +++ b/odb-tests/common/changelog/model-mysql-diff.xml @@ -0,0 +1 @@ +model-mysql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-mysql-patch.xml b/odb-tests/common/changelog/model-mysql-patch.xml new file mode 120000 index 0000000..9100280 --- /dev/null +++ b/odb-tests/common/changelog/model-mysql-patch.xml @@ -0,0 +1 @@ +model-mysql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-mysql.xml b/odb-tests/common/changelog/model-mysql.xml new file mode 100644 index 0000000..ffbcf8d --- /dev/null +++ b/odb-tests/common/changelog/model-mysql.xml @@ -0,0 +1,56 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="mysql" version="1"> + <model version="1"> + <table name="object" options="ENGINE=InnoDB" kind="object"> + <column name="id" type="INT" null="false"/> + <column name="num" type="INT" null="true" default="0" options="DUMMY=1"/> + <column name="v_x" type="INT" null="false"/> + <column name="v_y" type="INT" null="false"/> + <column name="o1_x" type="INT" null="true"/> + <column name="o1_y" type="INT" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1_x"/> + <column name="o1_y"/> + <references table="object1"> + <column name="id_x"/> + <column name="id_y"/> + </references> + </foreign-key> + <index name="num_i" type="UNIQUE" method="BTREE" options="DUMMY=1"> + <column name="num" options="DESC"/> + </index> + <index name="v_i"> + <column name="v_x"/> + <column name="v_y"/> + </index> + </table> + <table name="object_nums" options="ENGINE=InnoDB" kind="container"> + <column name="object_id" type="INT" null="false"/> + <column name="index" type="BIGINT UNSIGNED" null="false"/> + <column name="value" type="INT" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_id_i"> + <column name="object_id"/> + </index> + <index name="index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" options="ENGINE=InnoDB" kind="object"> + <column name="id_x" type="INT" null="false"/> + <column name="id_y" type="INT" null="false"/> + <column name="num" type="INT" null="false"/> + <primary-key> + <column name="id_x"/> + <column name="id_y"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/model-oracle-diff.xml b/odb-tests/common/changelog/model-oracle-diff.xml new file mode 120000 index 0000000..36e4479 --- /dev/null +++ b/odb-tests/common/changelog/model-oracle-diff.xml @@ -0,0 +1 @@ +model-oracle.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-oracle-patch.xml b/odb-tests/common/changelog/model-oracle-patch.xml new file mode 120000 index 0000000..36e4479 --- /dev/null +++ b/odb-tests/common/changelog/model-oracle-patch.xml @@ -0,0 +1 @@ +model-oracle.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-oracle.xml b/odb-tests/common/changelog/model-oracle.xml new file mode 100644 index 0000000..1824690 --- /dev/null +++ b/odb-tests/common/changelog/model-oracle.xml @@ -0,0 +1,56 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="oracle" version="1"> + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="true" default="0" options="DUMMY=1"/> + <column name="v_x" type="NUMBER(10)" null="false"/> + <column name="v_y" type="NUMBER(10)" null="false"/> + <column name="o1_x" type="NUMBER(10)" null="true"/> + <column name="o1_y" type="NUMBER(10)" null="true"/> + <primary-key auto="true" sequence="object_seq"> + <column name="id"/> + </primary-key> + <foreign-key name="object_o1_fk" deferrable="DEFERRED"> + <column name="o1_x"/> + <column name="o1_y"/> + <references table="object1"> + <column name="id_x"/> + <column name="id_y"/> + </references> + </foreign-key> + <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1"> + <column name="num" options="DESC"/> + </index> + <index name="object_v_i"> + <column name="v_x"/> + <column name="v_y"/> + </index> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="NUMBER(10)" null="false"/> + <column name="index" type="NUMBER(20)" null="false"/> + <column name="value" type="NUMBER(10)" null="false"/> + <foreign-key name="object_nums_object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id_x" type="NUMBER(10)" null="false"/> + <column name="id_y" type="NUMBER(10)" null="false"/> + <column name="num" type="NUMBER(10)" null="false"/> + <primary-key> + <column name="id_x"/> + <column name="id_y"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/model-pgsql-diff.xml b/odb-tests/common/changelog/model-pgsql-diff.xml new file mode 120000 index 0000000..b39ce26 --- /dev/null +++ b/odb-tests/common/changelog/model-pgsql-diff.xml @@ -0,0 +1 @@ +model-pgsql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-pgsql-patch.xml b/odb-tests/common/changelog/model-pgsql-patch.xml new file mode 120000 index 0000000..b39ce26 --- /dev/null +++ b/odb-tests/common/changelog/model-pgsql-patch.xml @@ -0,0 +1 @@ +model-pgsql.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-pgsql.xml b/odb-tests/common/changelog/model-pgsql.xml new file mode 100644 index 0000000..13aee6b --- /dev/null +++ b/odb-tests/common/changelog/model-pgsql.xml @@ -0,0 +1,56 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="pgsql" version="1"> + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="true" default="0" options="DUMMY=1"/> + <column name="v_x" type="INTEGER" null="false"/> + <column name="v_y" type="INTEGER" null="false"/> + <column name="o1_x" type="INTEGER" null="true"/> + <column name="o1_y" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1_x"/> + <column name="o1_y"/> + <references table="object1"> + <column name="id_x"/> + <column name="id_y"/> + </references> + </foreign-key> + <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1"> + <column name="num" options="DESC"/> + </index> + <index name="object_v_i"> + <column name="v_x"/> + <column name="v_y"/> + </index> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="BIGINT" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id_x" type="INTEGER" null="false"/> + <column name="id_y" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id_x"/> + <column name="id_y"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/model-sqlite-diff.xml b/odb-tests/common/changelog/model-sqlite-diff.xml new file mode 120000 index 0000000..3454f51 --- /dev/null +++ b/odb-tests/common/changelog/model-sqlite-diff.xml @@ -0,0 +1 @@ +model-sqlite.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-sqlite-patch.xml b/odb-tests/common/changelog/model-sqlite-patch.xml new file mode 120000 index 0000000..3454f51 --- /dev/null +++ b/odb-tests/common/changelog/model-sqlite-patch.xml @@ -0,0 +1 @@ +model-sqlite.xml
\ No newline at end of file diff --git a/odb-tests/common/changelog/model-sqlite.xml b/odb-tests/common/changelog/model-sqlite.xml new file mode 100644 index 0000000..d0199ed --- /dev/null +++ b/odb-tests/common/changelog/model-sqlite.xml @@ -0,0 +1,56 @@ +<changelog xmlns="http://www.codesynthesis.com/xmlns/odb/changelog" database="sqlite" version="1"> + <model version="1"> + <table name="object" kind="object"> + <column name="id" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="true" default="0" options="DUMMY=1"/> + <column name="v_x" type="INTEGER" null="false"/> + <column name="v_y" type="INTEGER" null="false"/> + <column name="o1_x" type="INTEGER" null="true"/> + <column name="o1_y" type="INTEGER" null="true"/> + <primary-key auto="true"> + <column name="id"/> + </primary-key> + <foreign-key name="o1_fk" deferrable="DEFERRED"> + <column name="o1_x"/> + <column name="o1_y"/> + <references table="object1"> + <column name="id_x"/> + <column name="id_y"/> + </references> + </foreign-key> + <index name="object_num_i" type="UNIQUE" method="BTREE" options="DUMMY=1"> + <column name="num" options="DESC"/> + </index> + <index name="object_v_i"> + <column name="v_x"/> + <column name="v_y"/> + </index> + </table> + <table name="object_nums" kind="container"> + <column name="object_id" type="INTEGER" null="false"/> + <column name="index" type="INTEGER" null="false"/> + <column name="value" type="INTEGER" null="false"/> + <foreign-key name="object_id_fk" on-delete="CASCADE"> + <column name="object_id"/> + <references table="object"> + <column name="id"/> + </references> + </foreign-key> + <index name="object_nums_object_id_i"> + <column name="object_id"/> + </index> + <index name="object_nums_index_i"> + <column name="index"/> + </index> + </table> + <table name="object1" kind="object"> + <column name="id_x" type="INTEGER" null="false"/> + <column name="id_y" type="INTEGER" null="false"/> + <column name="num" type="INTEGER" null="false"/> + <primary-key> + <column name="id_x"/> + <column name="id_y"/> + </primary-key> + </table> + </model> +</changelog> diff --git a/odb-tests/common/changelog/model.hxx b/odb-tests/common/changelog/model.hxx new file mode 100644 index 0000000..aa8891a --- /dev/null +++ b/odb-tests/common/changelog/model.hxx @@ -0,0 +1,46 @@ +// file : common/changelog/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_HXX +#define MODEL_HXX + +#include <vector> + +#pragma db model version(1, 1, open) + +#pragma db value +struct value +{ + int x; + int y; +}; + +struct object1; + +#pragma db object +struct object +{ + #pragma db id auto + int id; + + #pragma db null default(0) options("DUMMY=1") + int num; + + #pragma db index unique member(num, "DESC") method("BTREE") options("DUMMY=1") + + #pragma db index + value v; // Multi-column. + + std::vector<int> nums; + object1* o1; +}; + +#pragma db object +struct object1 +{ + #pragma db id + value id; // Multi-column. + int num; +}; + +#endif // MODEL_HXX diff --git a/odb-tests/common/changelog/testscript b/odb-tests/common/changelog/testscript new file mode 100644 index 0000000..60eadae --- /dev/null +++ b/odb-tests/common/changelog/testscript @@ -0,0 +1,104 @@ +# file : common/changelog/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +headers = [paths] $path_search($src_base/*.hxx) + +odb_options = --generate-schema-only \ + --schema-format sql \ + --suppress-migration \ + --options-file $out_base/odb.options + +: mysql +: +if $mysql +{ + odb_options += --database 'mysql' --changelog-dir $~ + + for h: $headers + n = $base($leaf($h)) + + $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql + + $* $odb_options -DBVER=1 -DCVER=2 $h + diff $src_base/$n-mysql-diff.xml $(n).xml + + $* $odb_options -DBVER=2 -DCVER=3 $h + diff $src_base/$n-mysql-patch.xml $(n).xml + end +} + +: sqlite +: +if $sqlite +{ + odb_options += --database 'sqlite' --changelog-dir $~ + + for h: $headers + n = $base($leaf($h)) + + $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql + + $* $odb_options -DBVER=1 -DCVER=2 $h + diff $src_base/$n-sqlite-diff.xml $(n).xml + + $* $odb_options -DBVER=2 -DCVER=3 $h + diff $src_base/$n-sqlite-patch.xml $(n).xml + end +} + +: pgsql +: +if $pgsql +{ + odb_options += --database 'pgsql' --changelog-dir $~ + + for h: $headers + n = $base($leaf($h)) + + $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql + + $* $odb_options -DBVER=1 -DCVER=2 $h + diff $src_base/$n-pgsql-diff.xml $(n).xml + + $* $odb_options -DBVER=2 -DCVER=3 $h + diff $src_base/$n-pgsql-patch.xml $(n).xml + end +} + +: oracle +: +if $oracle +{ + odb_options += --database 'oracle' --changelog-dir $~ + + for h: $headers + n = $base($leaf($h)) + + $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql + + $* $odb_options -DBVER=1 -DCVER=2 $h + diff $src_base/$n-oracle-diff.xml $(n).xml + + $* $odb_options -DBVER=2 -DCVER=3 $h + diff $src_base/$n-oracle-patch.xml $(n).xml + end +} + +: mssql +: +if $mssql +{ + odb_options += --database 'mssql' --changelog-dir $~ + + for h: $headers + n = $base($leaf($h)) + + $* $odb_options -DBVER=1 -DCVER=1 --init-changelog $h &$(n).xml &$(n).sql + + $* $odb_options -DBVER=1 -DCVER=2 $h + diff $src_base/$n-mssql-diff.xml $(n).xml + + $* $odb_options -DBVER=2 -DCVER=3 $h + diff $src_base/$n-mssql-patch.xml $(n).xml + end +} diff --git a/odb-tests/common/circular/buildfile b/odb-tests/common/circular/buildfile new file mode 100644 index 0000000..2e793b9 --- /dev/null +++ b/odb-tests/common/circular/buildfile @@ -0,0 +1,9 @@ +# file : common/circular/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +./: {*/ -multiple/} + +# We cannot support this case in multi-database support since we need to +# generate extern template involving classes that participate in the cycle. +# +./: multiple/: include = (!$multi) diff --git a/odb-tests/common/circular/multiple/.gitignore b/odb-tests/common/circular/multiple/.gitignore new file mode 100644 index 0000000..5d39d39 --- /dev/null +++ b/odb-tests/common/circular/multiple/.gitignore @@ -0,0 +1,6 @@ +# ODB-generated files. +# +test1-odb.?xx +test1-odb-*.?xx +test2-odb.?xx +test2-odb-*.?xx diff --git a/odb-tests/common/circular/multiple/buildfile b/odb-tests/common/circular/multiple/buildfile new file mode 100644 index 0000000..51a1191 --- /dev/null +++ b/odb-tests/common/circular/multiple/buildfile @@ -0,0 +1,49 @@ +# file : common/circular/multiple/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = test1 test2 + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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 circular_m_ \ + --generate-schema \ + --generate-query \ + --schema-format embedded + +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/circular/multiple/driver.cxx b/odb-tests/common/circular/multiple/driver.cxx new file mode 100644 index 0000000..4887ac2 --- /dev/null +++ b/odb-tests/common/circular/multiple/driver.cxx @@ -0,0 +1,69 @@ +// file : common/circular/multiple/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test cases of circular dependencies between persistent classes, multiple +// files version. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/connection.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/common.hxx> + +#include "test1.hxx" +#include "test2.hxx" + +#include "test2-odb.hxx" +#include "test1-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv, false)); + + // Create the database schema. + // + { + connection_ptr c (db->connection ()); + + // Temporarily disable foreign key constraints for MySQL and SQLite. + // For these databases this is the only way to drop circularly- + // dependant tables. + // + if (db->id () == odb::id_mysql) + c->execute ("SET FOREIGN_KEY_CHECKS=0"); + else if (db->id () == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=OFF"); + + transaction t (c->begin ()); + schema_catalog::create_schema (*db); + t.commit (); + + if (db->id () == odb::id_mysql) + c->execute ("SET FOREIGN_KEY_CHECKS=1"); + else if (db->id () == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=ON"); + } + + query<base> bq (query<base>::d->id != 0); + query<derived> dq (query<derived>::b->id != 0); + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/circular/multiple/test1.hxx b/odb-tests/common/circular/multiple/test1.hxx new file mode 100644 index 0000000..36df963 --- /dev/null +++ b/odb-tests/common/circular/multiple/test1.hxx @@ -0,0 +1,27 @@ +// file : common/circular/multiple/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#include <odb/core.hxx> + +#pragma db object +struct derived; + +#pragma db object polymorphic +struct base +{ + virtual ~base () {} + + #pragma db id + unsigned long id_; + + derived* d_; +}; + +#ifdef ODB_COMPILER +# include "test2.hxx" +#endif + +#endif // TEST1_HXX diff --git a/odb-tests/common/circular/multiple/test2.hxx b/odb-tests/common/circular/multiple/test2.hxx new file mode 100644 index 0000000..49e9ed2 --- /dev/null +++ b/odb-tests/common/circular/multiple/test2.hxx @@ -0,0 +1,17 @@ +// file : common/circular/multiple/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#include <odb/core.hxx> + +#include "test1.hxx" + +#pragma db object +struct derived: base +{ + base* b_; +}; + +#endif // TEST2_HXX diff --git a/odb-tests/common/circular/multiple/testscript b/odb-tests/common/circular/multiple/testscript new file mode 100644 index 0000000..3a71341 --- /dev/null +++ b/odb-tests/common/circular/multiple/testscript @@ -0,0 +1,49 @@ +# file : common/circular/multiple/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $* +} diff --git a/odb-tests/common/circular/single/buildfile b/odb-tests/common/circular/single/buildfile new file mode 100644 index 0000000..740ce91 --- /dev/null +++ b/odb-tests/common/circular/single/buildfile @@ -0,0 +1,41 @@ +# file : common/circular/single/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 circular_s_ \ + --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/circular/single/driver.cxx b/odb-tests/common/circular/single/driver.cxx new file mode 100644 index 0000000..9bcb135 --- /dev/null +++ b/odb-tests/common/circular/single/driver.cxx @@ -0,0 +1,40 @@ +// file : common/circular/single/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test cases of circular dependencies between persistent classes, single +// file version. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + query<base> bq (query<base>::d->id != 0); + query<derived> dq (query<derived>::b->id != 0); + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/circular/single/test.hxx b/odb-tests/common/circular/single/test.hxx new file mode 100644 index 0000000..7f95dea --- /dev/null +++ b/odb-tests/common/circular/single/test.hxx @@ -0,0 +1,28 @@ +// file : common/circular/single/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <odb/core.hxx> + +struct derived; + +#pragma db object polymorphic +struct base +{ + virtual ~base () {} + + #pragma db id + unsigned long id_; + + derived* d_; +}; + +#pragma db object +struct derived: base +{ + base* b_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/circular/single/testscript b/odb-tests/common/circular/single/testscript new file mode 100644 index 0000000..480ef6d --- /dev/null +++ b/odb-tests/common/circular/single/testscript @@ -0,0 +1,53 @@ +# file : common/circular/single/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/composite/buildfile b/odb-tests/common/composite/buildfile new file mode 100644 index 0000000..0a60638 --- /dev/null +++ b/odb-tests/common/composite/buildfile @@ -0,0 +1,41 @@ +# file : common/composite/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_comp_ \ + --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/composite/driver.cxx b/odb-tests/common/composite/driver.cxx new file mode 100644 index 0000000..06b24b2 --- /dev/null +++ b/odb-tests/common/composite/driver.cxx @@ -0,0 +1,229 @@ +// file : common/composite/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test composite value types. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +using namespace std; +using namespace odb::core; + +#undef NDEBUG +#include <cassert> + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic composite functionality. + // + for (unsigned short i (0); i < 2; ++i) + { + using namespace test1; + + person p (1); + p.name_.first = "Joe"; + p.name_.last = "Dirt"; + p.name_.title = "Mr"; + p.name_.alias.first = "Anthony"; + p.name_.alias.last = "Clean"; + p.name_.nick = "Squeaky"; + p.name_.flags.nick = true; + p.name_.flags.alias = false; + p.age_ = 32; + + // persist + // + { + transaction t (db->begin ()); + db->persist (p); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<person> p1 (db->load<person> (1)); + t.commit (); + + assert (p == *p1); + } + + p.name_.title = "Mrs"; + p.name_.alias.first = "Anthonia"; + p.name_.flags.nick = false; + p.name_.flags.alias = true; + + // update + // + { + transaction t (db->begin ()); + db->update (p); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<person> p1 (db->load<person> (1)); + t.commit (); + + assert (p == *p1); + } + + typedef odb::query<person> query; + typedef odb::result<person> result; + + // query + // + { + transaction t (db->begin ()); + + result r (db->query<person> (query::name.first == "Joe")); + + assert (!r.empty ()); + assert (*r.begin () == p); + assert (size (r) == 1); + + t.commit (); + } + + // query + // + { + transaction t (db->begin ()); + + result r (db->query<person> (query::name.flags.alias)); + + assert (!r.empty ()); + assert (*r.begin () == p); + assert (size (r) == 1); + + t.commit (); + } + + // erase + // + if (i == 0) + { + transaction t (db->begin ()); + db->erase<person> (1); + t.commit (); + } + } + + // Test composite class template instantiation. + // + { + using namespace test2; + + object o (1); + + o.comp_.num = 123; + o.comp_.str = "abc"; + o.comp_.vec.push_back (int_str_pair (123, "abc")); + o.comp_.vec.push_back (int_str_pair (234, "bcd")); + o.comp_.vec.push_back (int_str_pair (345, "cde")); + + o.pair_.first = 123; + o.pair_.second = "abc"; + + o.vec_.push_back (int_str_pair (123, "abc")); + o.vec_.push_back (int_str_pair (234, "bcd")); + o.vec_.push_back (int_str_pair (345, "cde")); + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + t.commit (); + + assert (o == *o1); + } + } + + // Test empty column name. + // + { + using namespace test3; + + object o (1); + o.c_.str = "abc"; + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + t.commit (); + + assert (o == *o1); + } + } + + // Test composite definition inside object. + { + using namespace test4; + + object o (1); + o.str ("abc"); + o.x (123); + o.y (234); + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + t.commit (); + + assert (o == *o1); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/composite/test.hxx b/odb-tests/common/composite/test.hxx new file mode 100644 index 0000000..13b2025 --- /dev/null +++ b/odb-tests/common/composite/test.hxx @@ -0,0 +1,250 @@ +// file : common/composite/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> +#include <utility> // std::pair + +#include <odb/core.hxx> + +// Test basic composite functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db value + struct name + { + std::string first; + std::string last; + }; + + #pragma db value + struct name_title + { + std::string title; + }; + + #pragma db value + struct name_title_ex: name_title + { + // Test value types without data members. + }; + + #pragma db value + struct name_flags + { + bool nick; + bool alias; + }; + + #pragma db value + struct name_ex: name, name_title_ex + { + name alias; + std::string nick; + + #pragma db column("show_") + name_flags flags; + }; + + #pragma db object + struct person + { + person () {} + person (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db column("") + name_ex name_; + + unsigned short age_; + }; + + inline bool + operator== (const person& x, const person& y) + { + return x.id_ == y.id_ && + x.name_.first == y.name_.first&& + x.name_.last == y.name_.last && + x.name_.title == y.name_.title && + x.name_.alias.first == y.name_.alias.first && + x.name_.alias.last == y.name_.alias.last && + x.name_.nick == y.name_.nick && + x.name_.flags.nick == y.name_.flags.nick && + x.name_.flags.alias == y.name_.flags.alias && + x.age_ == y.age_; + } +} + +// Test composite class template instantiation. +// +#pragma db namespace table("t2_") +namespace test2 +{ + template <typename I, typename S> + struct comp + { + I num; + S str; + std::vector<std::pair<I, S> > vec; + }; + + template <typename I, typename S> + inline bool + operator== (const comp<I, S>& x, const comp<I, S>& y) + { + return x.num == y.num && x.str == y.str && x.vec == y.vec; + } + + typedef std::pair<int, std::string> int_str_pair; + #pragma db value(int_str_pair) + + // Make sure we use the name that was specified in the pragma. + // +#ifdef ODB_COMPILER + typedef comp<int, std::string> int_str_comp1; +#endif + + typedef comp<int, std::string> int_str_comp; + #pragma db value(int_str_comp) + + #pragma db object + struct object + { + object () {} + object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + comp<int, std::string> comp_; + std::pair<int, std::string> pair_; + std::vector<int_str_pair> vec_; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.id_ == y.id_ && + x.comp_ == y.comp_ && + x.pair_ == y.pair_ && + x.vec_ == y.vec_; + } +} + +// Test empty column name. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db value + struct comp + { + #pragma db column("") + std::string str; + }; + + #pragma db object + struct object + { + object () {} + object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + comp c_; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.id_ == y.id_ && x.c_.str == y.c_.str; + } +} + +// Test composite definition inside object. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + unsigned long id () const {return id_;} + + void str (const std::string& s) {c_.str = s;} + const std::string& str () const {return c_.str;} + + void x (int i) {p_.first = i;} + int x () const {return p_.first;} + + void y (int i) {p_.second = i;} + int y () const {return p_.second;} + + private: + friend class odb::access; + + #pragma db id + unsigned long id_; + + #pragma db value + struct comp + { + std::string str; + }; + + comp c_; + + typedef std::pair<int, int> int_pair; + #pragma db value(int_pair) + + int_pair p_; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.id () == y.id () && x.str () == y.str () && + x.x () == y.x () && x.y () == y.y (); + } +} + +// Test composite name clashes in query columns (compilation test) +// +#pragma db namespace table("t5_") +namespace test5 +{ + // Class-member conflict. + // + #pragma db value + struct value {int value_;}; + + // Class-class conflict. + // + #pragma db value + struct inner {int value;}; + + #pragma db value + struct outer {inner value;}; + + #pragma db object + struct object + { + #pragma db id + int id; + + outer value; + test5::value v; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/composite/testscript b/odb-tests/common/composite/testscript new file mode 100644 index 0000000..f890985 --- /dev/null +++ b/odb-tests/common/composite/testscript @@ -0,0 +1,53 @@ +# file : common/composite/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/const-member/buildfile b/odb-tests/common/const-member/buildfile new file mode 100644 index 0000000..868f7fd --- /dev/null +++ b/odb-tests/common/const-member/buildfile @@ -0,0 +1,40 @@ +# file : common/const-member/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 constm_ \ + --generate-schema + +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/const-member/driver.cxx b/odb-tests/common/const-member/driver.cxx new file mode 100644 index 0000000..0c71dfa --- /dev/null +++ b/odb-tests/common/const-member/driver.cxx @@ -0,0 +1,119 @@ +// file : common/const-member/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test const data members. The readonly test tests that const +// members are automatically treated as read-only. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Const ids. + // + { + const_id o (1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load<const_id> (1, o); + t.commit (); + assert (o.id == 1); + } + } + + { + { + const_auto_id o; + transaction t (db->begin ()); + db->persist (o); + t.commit (); + assert (o.id == 1); + } + + { + transaction t (db->begin ()); + unique_ptr<const_auto_id> o (db->load<const_auto_id> (1)); + t.commit (); + assert (o->id == 1); + } + } + + // Container. + // + { + container o (1, 1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<container> o (db->load<container> (1)); + t.commit (); + + assert (o->ccom.vec.size () == 1 && o->ccom.vec[0] == 1 && + o->ccom.cvec.size () == 1 && o->ccom.cvec[0] == 1 && + o->cvec.size () == 1 && o->cvec[0] == 1); + } + } + + // Wrapper. + // + { + wrapper o (1, "abc", 1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<wrapper> o (db->load<wrapper> (1)); + t.commit (); + + assert (*o->str == "abc" && + o->com->str == "abc" && o->com->num == 1 && + o->com->vec.size () == 1 && o->com->vec[0] == 1 && + o->vec->size () == 1 && (*o->vec)[0] == 1); + } + } + + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/const-member/test.hxx b/odb-tests/common/const-member/test.hxx new file mode 100644 index 0000000..ab75c55 --- /dev/null +++ b/odb-tests/common/const-member/test.hxx @@ -0,0 +1,109 @@ +// file : common/const-member/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> +#include <string> +#include <memory> // std::auto_ptr + +#include <odb/core.hxx> + +// Const ids. +// +#pragma db object +struct const_id +{ + const_id (unsigned long i): id (i) {} + const_id (): id (0) {} + + #pragma db id + const unsigned long id; +}; + +#pragma db object +struct const_auto_id +{ + const_auto_id (): id (0) {} + + #pragma db id auto + const unsigned long id; +}; + +// Container. +// +#pragma db value +struct container_value +{ + container_value (unsigned long x) + { + vec.push_back (x); + const_cast<std::vector<unsigned long>&> (cvec).push_back (x); + } + + container_value () {} + + std::vector<unsigned long> vec; + const std::vector<unsigned long> cvec; +}; + +#pragma db object +struct container +{ + container (unsigned long i, unsigned long x) + : id (i), ccom (x) + { + const_cast<std::vector<unsigned long>&> (cvec).push_back (x); + } + + container () {} + + #pragma db id + unsigned long id; + + const container_value ccom; + const std::vector<unsigned long> cvec; +}; + +// Wrapper. +// +#pragma db value +struct wrapped_value +{ + wrapped_value (const std::string& s, unsigned long n) + : str (s), num (n) + { + vec.push_back (n); + } + + wrapped_value () {} + + const std::string str; + unsigned long num; + std::vector<unsigned long> vec; +}; + +#pragma db object +struct wrapper +{ + wrapper (unsigned long i, const std::string& s, unsigned long n) + : id (i), + str (new std::string (s)), + com (new wrapped_value (s, n)), + vec (new std::vector<unsigned long>) + { + const_cast<std::vector<unsigned long>&> (*vec).push_back (n); + } + + wrapper () {} + + #pragma db id + unsigned long id; + + const std::unique_ptr<const std::string> str; + const std::unique_ptr<const wrapped_value> com; + const std::unique_ptr<const std::vector<unsigned long>> vec; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/const-member/testscript b/odb-tests/common/const-member/testscript new file mode 100644 index 0000000..48a1456 --- /dev/null +++ b/odb-tests/common/const-member/testscript @@ -0,0 +1,53 @@ +# file : common/const-member/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/const-object/buildfile b/odb-tests/common/const-object/buildfile new file mode 100644 index 0000000..853c831 --- /dev/null +++ b/odb-tests/common/const-object/buildfile @@ -0,0 +1,41 @@ +# file : common/const-object/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 consto_ \ + --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/const-object/driver.cxx b/odb-tests/common/const-object/driver.cxx new file mode 100644 index 0000000..7ef48ee --- /dev/null +++ b/odb-tests/common/const-object/driver.cxx @@ -0,0 +1,216 @@ +// file : common/const-object/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test database operations with const objects. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + aggr a (1); + aggr ca_ (2); // o1 and o2 are NULL + const aggr& ca (ca_); + + obj1* o1 (new obj1 (1)); + obj1* co1_ (new obj1 (2)); + const obj1* co1 (co1_); + a.o1 = co1; + + unique_ptr<obj2> o2 (new obj2 (1)); + obj2* co2_ (new obj2 (2)); + a.o2.reset (co2_); + unique_ptr<const obj2>& co2 (a.o2); + + // persist via references + // + { + transaction t (db->begin ()); + db->persist (*o1); + db->persist (*co1); + db->persist (*o2); + db->persist (*co2); + db->persist (a); + db->persist (ca); + t.commit (); + } + + // persist via pointers + // + o1->id += 2; + co1_->id += 2; + o2->id += 2; + co2_->id += 2; + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (co1); + db->persist (o2); + db->persist (co2); + t.commit (); + } + + // load & compare + // + { + transaction t (db->begin ()); + + unique_ptr<aggr> a (db->load<aggr> (1)); + unique_ptr<const aggr> ca (db->load<aggr> (2)); + + t.commit (); + + assert (a->o1->id == 2); + assert (a->o2->id == 2); + + assert (ca->o1 == 0); + assert (ca->o2.get () == 0); + } + + // update via references + // + { + transaction t (db->begin ()); + db->update (*o1); + db->update (*co1); + db->update (*o2); + db->update (*co2); + db->update (a); + db->update (ca); + t.commit (); + } + + // update via pointers + // + { + transaction t (db->begin ()); + db->update (o1); + db->update (co1); + db->update (o2); + db->update (co2); + t.commit (); + } + + // query + // + typedef odb::query<obj1> query1; + typedef odb::query<obj2> query2; + + typedef odb::result<const obj1> result1; + typedef odb::result<const obj2> result2; + + { + transaction t (db->begin ()); + result1 r1 (db->query<obj1> (query1::id < 3)); + // odb::result<obj1> ur (r1); // error + size_t n1 (0); + + for (result1::iterator i (r1.begin ()); i != r1.end (); ++i) + { + // i->f (); // error + i->cf (); + // obj1* p (i.load ()); // error + const obj1* p (i.load ()); + obj1 o (0); + i.load (o); + assert (p->id == o.id); + delete p; + n1++; + } + + assert (n1 == 2); + + result2 r2 (db->query<obj2> (query2::id < 3)); + size_t n2 (0); + + for (result2::iterator i (r2.begin ()); i != r2.end (); ++i) + { + // i->f (); // error + i->cf (); + //unique_ptr<obj2> p (i.load ()); // error + unique_ptr<const obj2> p (i.load ()); + obj2 o (0); + i.load (o); + assert (p->id == o.id); + n2++; + } + + assert (n2 == 2); + + t.commit (); + } + + // erase via references + // + { + transaction t (db->begin ()); + db->erase (*o1); + db->erase (*co1); + db->erase (*o2); + db->erase (*co2); + db->erase (a); + db->erase (ca); + t.commit (); + } + + // erase via pointers + // + o1->id -= 2; + co1_->id -= 2; + o2->id -= 2; + co2_->id -= 2; + + { + transaction t (db->begin ()); + db->erase (o1); + db->erase (co1); + db->erase (o2); + db->erase (co2); + t.commit (); + } + + // Test session and const/non-const object handling + // + { + session s; + transaction t (db->begin ()); + + obj1 o1 (1); + const obj1& co1 (o1); + db->persist (co1); + + assert (db->load<obj1> (1) == &o1); + + t.commit (); + } + + delete o1; + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/const-object/test.hxx b/odb-tests/common/const-object/test.hxx new file mode 100644 index 0000000..4e66231 --- /dev/null +++ b/odb-tests/common/const-object/test.hxx @@ -0,0 +1,51 @@ +// file : common/const-object/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <memory> +#include <odb/core.hxx> + +#pragma db object pointer (obj1*) session +struct obj1 +{ + obj1 () {} + obj1 (int i): id (i) {} + + #pragma db id + int id; + + void f () {} + void cf () const {} +}; + +#pragma db object pointer (std::unique_ptr<obj2>) +struct obj2 +{ + obj2 () {} + obj2 (int i): id (i) {} + + #pragma db id + int id; + + void f () {} + void cf () const {} +}; + +#pragma db object +struct aggr +{ + aggr (int i): id (i), o1 (0) {} + aggr (): o1 (0) {} + ~aggr () {delete o1;} + + #pragma db id + int id; + + const obj1* o1; + + std::unique_ptr<const obj2> o2; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/const-object/testscript b/odb-tests/common/const-object/testscript new file mode 100644 index 0000000..928bad0 --- /dev/null +++ b/odb-tests/common/const-object/testscript @@ -0,0 +1,53 @@ +# file : common/const-object/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/container/basics/buildfile b/odb-tests/common/container/basics/buildfile new file mode 100644 index 0000000..f83444e --- /dev/null +++ b/odb-tests/common/container/basics/buildfile @@ -0,0 +1,40 @@ +# file : common/container/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_cont_bs_ \ + --generate-schema + +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/container/basics/driver.cxx b/odb-tests/common/container/basics/driver.cxx new file mode 100644 index 0000000..14e1984 --- /dev/null +++ b/odb-tests/common/container/basics/driver.cxx @@ -0,0 +1,523 @@ +// file : common/container/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test basic container persistence. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + for (unsigned short i (0); i < 2; ++i) + { + object empty ("empty"), med ("medium"), full ("full"); + + // + // empty + // + + empty.num = 0; + empty.str = ""; + + // array + // + empty.na[0] = 123; + empty.na[1] = 234; + empty.na[2] = 345; + + empty.sa[0] = "aaa"; + empty.sa[1] = "bbbb"; + empty.sa[2] = "ccccc"; + + empty.ca[0] = comp (123, "aaa"); + empty.ca[1] = comp (234, "bbbb"); + empty.ca[2] = comp (345, "ccccc"); + + + // + // med + // + + med.num = 999; + med.str = "xxx"; + + // vector + // + med.nv.push_back (123); + med.nv.push_back (234); + + med.sv.push_back ("aaa"); + med.sv.push_back ("bbbb"); + + med.cv.push_back (comp (123, "aaa")); + med.cv.push_back (comp (234, "bbbb")); + + med.uv.push_back (123); + med.uv.push_back (234); + + // list + // + med.sl.push_back ("aaa"); + med.sl.push_back ("bbbb"); + + // deque + // + med.nd.push_back (123); + med.nd.push_back (234); + + // set + // + med.ns.insert (123); + med.ns.insert (234); + + med.ss.insert ("aaa"); + med.ss.insert ("bbbb"); + + med.cs.insert (comp (123, "aaa")); + med.cs.insert (comp (234, "bbbb")); + + // map + // + med.nsm[123] = "aaa"; + med.nsm[234] = "bbbb"; + + med.snm["aaa"] = 123; + med.snm["bbbb"] = 234; + + med.ncm[123] = comp (123, "aaa"); + med.ncm[234] = comp (234, "bbbb"); + + med.csm[comp (123, "aaa")] = "aaa"; + med.csm[comp (234, "bbbb")] = "bbbb"; + + // array + // + med.na[0] = 123; + med.na[1] = 234; + med.na[2] = 345; + + med.sa[0] = "aaa"; + med.sa[1] = "bbbb"; + med.sa[2] = "ccccc"; + + med.ca[0] = comp (123, "aaa"); + med.ca[1] = comp (234, "bbbb"); + med.ca[2] = comp (345, "ccccc"); + + // forward_list + // + med.nfl.push_front (234); + med.nfl.push_front (123); + + med.sfl.push_front ("bbbb"); + med.sfl.push_front ("aaa"); + + med.cfl.push_front (comp (234, "bbbb")); + med.cfl.push_front (comp (123, "aaa")); + + // unordered_set + // + med.nus.insert (123); + med.nus.insert (234); + + med.sus.insert ("aaa"); + med.sus.insert ("bbbb"); + + med.cus.insert (comp (123, "aaa")); + med.cus.insert (comp (234, "bbbb")); + + // unordered_map + // + med.nsum[123] = "aaa"; + med.nsum[234] = "bbbb"; + + med.snum["aaa"] = 123; + med.snum["bbbb"] = 234; + + med.ncum[123] = comp (123, "aaa"); + med.ncum[234] = comp (234, "bbbb"); + + med.csum[comp (123, "aaa")] = "aaa"; + med.csum[comp (234, "bbbb")] = "bbbb"; + + // + // full + // + + full.num = 9999; + full.str = "xxxx"; + + // vector + // + full.nv.push_back (1234); + full.nv.push_back (2345); + full.nv.push_back (3456); + + full.sv.push_back ("aaaa"); + full.sv.push_back ("bbbbb"); + full.sv.push_back ("cccccc"); + + full.cv.push_back (comp (1234, "aaaa")); + full.cv.push_back (comp (2345, "bbbbb")); + full.cv.push_back (comp (3456, "cccccc")); + + full.uv.push_back (1234); + full.uv.push_back (2345); + full.uv.push_back (3456); + + // list + // + full.sl.push_back ("aaaa"); + full.sl.push_back ("bbbbb"); + full.sl.push_back ("cccccc"); + + // deque + // + full.nd.push_back (1234); + full.nd.push_back (2345); + full.nd.push_back (3456); + + // set + // + full.ns.insert (1234); + full.ns.insert (2345); + full.ns.insert (3456); + + full.ss.insert ("aaaa"); + full.ss.insert ("bbbbb"); + full.ss.insert ("cccccc"); + + full.cs.insert (comp (1234, "aaaa")); + full.cs.insert (comp (2345, "bbbbb")); + full.cs.insert (comp (3456, "cccccc")); + + // map + // + full.nsm[1234] = "aaaa"; + full.nsm[2345] = "bbbbb"; + full.nsm[3456] = "cccccc"; + + full.snm["aaaa"] = 1234; + full.snm["bbbbb"] = 2345; + full.snm["cccccc"] = 3456; + + full.ncm[1234] = comp (1234, "aaaa"); + full.ncm[2345] = comp (2345, "bbbbb"); + full.ncm[3456] = comp (3456, "cccccc"); + + full.csm[comp (1234, "aaaa")] = "aaaa"; + full.csm[comp (2345, "bbbbb")] = "bbbbb"; + full.csm[comp (3456, "cccccc")] = "cccccc"; + + // array + // + full.na[0] = 123; + full.na[1] = 234; + full.na[2] = 345; + + full.sa[0] = "aaa"; + full.sa[1] = "bbbb"; + full.sa[2] = "ccccc"; + + full.ca[0] = comp (123, "aaa"); + full.ca[1] = comp (234, "bbbb"); + full.ca[2] = comp (345, "ccccc"); + + // forward_list + // + full.nfl.push_front (345); + full.nfl.push_front (234); + full.nfl.push_front (123); + + full.sfl.push_front ("ccccc"); + full.sfl.push_front ("bbbb"); + full.sfl.push_front ("aaa"); + + full.cfl.push_front (comp (345, "ccccc")); + full.cfl.push_front (comp (234, "bbbb")); + full.cfl.push_front (comp (123, "aaa")); + + // unordered_set + // + full.nus.insert (1234); + full.nus.insert (2345); + full.nus.insert (3456); + + full.sus.insert ("aaaa"); + full.sus.insert ("bbbbb"); + full.sus.insert ("cccccc"); + + full.cus.insert (comp (1234, "aaaa")); + full.cus.insert (comp (2345, "bbbbb")); + full.cus.insert (comp (3456, "cccccc")); + + // unordered_map + // + full.nsum[1234] = "aaaa"; + full.nsum[2345] = "bbbbb"; + full.nsum[3456] = "cccccc"; + + full.snum["aaaa"] = 1234; + full.snum["bbbbb"] = 2345; + full.snum["cccccc"] = 3456; + + full.ncum[1234] = comp (1234, "aaaa"); + full.ncum[2345] = comp (2345, "bbbbb"); + full.ncum[3456] = comp (3456, "cccccc"); + + full.csum[comp (1234, "aaaa")] = "aaaa"; + full.csum[comp (2345, "bbbbb")] = "bbbbb"; + full.csum[comp (3456, "cccccc")] = "cccccc"; + + // persist + // + { + transaction t (db->begin ()); + db->persist (empty); + db->persist (med); + db->persist (full); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> e (db->load<object> ("empty")); + unique_ptr<object> m (db->load<object> ("medium")); + unique_ptr<object> f (db->load<object> ("full")); + t.commit (); + + assert (empty == *e); + assert (med == *m); + assert (full == *f); + } + + // + // empty + // + + empty.num = 99; + empty.str = "xx"; + + empty.nv.push_back (12); + empty.sv.push_back ("aa"); + empty.cv.push_back (comp (12, "aa")); + empty.uv.push_back (12); + empty.sl.push_back ("aa"); + empty.nd.push_back (12); + + empty.ns.insert (12); + empty.ss.insert ("aa"); + empty.cs.insert (comp (12, "aa")); + + empty.nsm[12] = "aa"; + empty.snm["aa"] = 12; + empty.ncm[12] = comp (12, "aa"); + empty.csm[comp (12, "aa")] = "aa"; + + empty.nfl.push_front (12); + empty.sfl.push_front ("aa"); + empty.cfl.push_front (comp (12, "aa")); + + empty.nus.insert (12); + empty.sus.insert ("aa"); + empty.cus.insert (comp (12, "aa")); + + empty.nsum[12] = "aa"; + empty.snum["aa"] = 12; + empty.ncum[12] = comp (12, "aa"); + empty.csum[comp (12, "aa")] = "aa"; + + // + // med + // + + med.num = 0; + med.str = ""; + + med.nv.clear (); + med.sv.clear (); + med.cv.clear (); + med.uv.clear (); + + med.sl.clear (); + + med.nd.clear (); + + med.ns.clear (); + med.ss.clear (); + med.cs.clear (); + + med.nsm.clear (); + med.snm.clear (); + med.ncm.clear (); + med.csm.clear (); + + med.nfl.clear (); + med.sfl.clear (); + med.cfl.clear (); + + med.nus.clear (); + med.sus.clear (); + med.cus.clear (); + + med.nsum.clear (); + med.snum.clear (); + med.ncum.clear (); + med.csum.clear (); + + // + // full + // + + full.num++; + full.str += "x"; + + // vector + // + full.nv.back ()++; + full.nv.push_back (4567); + + full.sv.back () += "c"; + full.sv.push_back ("ddddddd"); + + full.cv.back ().num++; + full.cv.back ().str += "c"; + full.cv.push_back (comp (4567, "ddddddd")); + + full.uv.back ()++; + full.uv.push_back (4567); + + // list + // + full.sl.back () += "c"; + full.sl.push_back ("ddddddd"); + + // deque + // + full.nd.push_front (456); + + // set + // + full.ns.insert (4567); + full.ss.insert ("ddddddd"); + full.cs.insert (comp (4567, "ddddddd")); + + // map + // + full.nsm[3456] += 'c'; + full.nsm[4567] = "ddddddd"; + + full.snm["cccccc"]++; + full.snm["ddddddd"] = 4567; + + full.ncm[3456].num++; + full.ncm[3456].str += 'c'; + full.ncm[4567] = comp (4567, "ddddddd"); + + full.csm[comp (3456, "cccccc")] += "c"; + full.csm[comp (4567, "ddddddd")] = "ddddddd"; + + // array + // + full.na[0]++; + full.sa[0] += 'a'; + full.ca[0].num++; + full.ca[0].str += 'a'; + + // forward_list + // + full.nfl.front ()++; + full.nfl.push_front (4567); + + full.sfl.front () += 'a'; + full.sfl.push_front ("ddddddd"); + + full.cfl.front ().num++; + full.cfl.front ().str += 'a'; + full.cfl.push_front (comp (4567, "ddddddd")); + + // unordered_set + // + full.nus.insert (4567); + full.sus.insert ("ddddddd1"); // 1 is to preserve order in VC++ 10. + full.cus.insert (comp (4567, "ddddddd1")); + + // unordered_map + // + full.nsum[3456] += 'c'; + full.nsum[4567] = "ddddddd"; + + full.snum["cccccc"]++; + full.snum["ddddddd1"] = 4567; + + full.ncum[3456].num++; + full.ncum[3456].str += 'c'; + full.ncum[4567] = comp (4567, "ddddddd"); + + full.csum[comp (3456, "cccccc")] += "c"; + full.csum[comp (4567, "ddddddd1")] = "ddddddd"; + + // update + // + { + transaction t (db->begin ()); + db->update (empty); + db->update (med); + db->update (full); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> e (db->load<object> ("empty")); + unique_ptr<object> m (db->load<object> ("medium")); + unique_ptr<object> f (db->load<object> ("full")); + t.commit (); + + assert (empty == *e); + assert (med == *m); + assert (full == *f); + } + + // erase + // + if (i == 0) + { + transaction t (db->begin ()); + db->erase<object> ("empty"); + db->erase<object> ("medium"); + db->erase<object> ("full"); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/container/basics/test.hxx b/odb-tests/common/container/basics/test.hxx new file mode 100644 index 0000000..e8e329e --- /dev/null +++ b/odb-tests/common/container/basics/test.hxx @@ -0,0 +1,245 @@ +// file : common/container/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <map> +#include <set> +#include <list> +#include <vector> +#include <deque> +#include <array> +#include <string> +#include <forward_list> +#include <unordered_map> +#include <unordered_set> + +#include <odb/core.hxx> + +#pragma db value +struct comp +{ + comp () {} + comp (int n, const std::string& s) : num (n), str (s) {} + + #pragma db column("number") + int num = 0; + std::string str; +}; + +inline bool +operator== (const comp& x, const comp& y) +{ + return x.num == y.num && x.str == y.str; +} + +inline bool +operator!= (const comp& x, const comp& y) +{ + return !(x == y); +} + +inline bool +operator< (const comp& x, const comp& y) +{ + return x.num != y.num ? x.num < y.num : x.str < y.str; +} + +typedef std::list<std::string> str_list; +typedef std::deque<int> num_deque; + +typedef std::vector<int> num_vector; +typedef std::vector<std::string> str_vector; + +typedef std::set<int> num_set; +typedef std::set<std::string> str_set; +typedef std::set<comp> comp_set; + +typedef std::map<int, std::string> num_str_map; +typedef std::map<std::string, int> str_num_map; +typedef std::map<int, comp> num_comp_map; +typedef std::map<comp, std::string> comp_str_map; + +struct comp_hash +{ + std::size_t + operator() (const comp& x) const {return nh (x.num) + sh (x.str);} + + std::hash<int> nh; + std::hash<std::string> sh; +}; + +typedef std::array<int, 3> num_array; +typedef std::array<std::string, 3> str_array; +typedef std::array<comp, 3> comp_array; + +typedef std::forward_list<int> num_flist; +typedef std::forward_list<std::string> str_flist; +typedef std::forward_list<comp> comp_flist; + +typedef std::unordered_set<int> num_uset; +typedef std::unordered_set<std::string> str_uset; +typedef std::unordered_set<comp, comp_hash> comp_uset; + +typedef std::unordered_map<int, std::string> num_str_umap; +typedef std::unordered_map<std::string, int> str_num_umap; +typedef std::unordered_map<int, comp> num_comp_umap; +typedef std::unordered_map<comp, std::string, comp_hash> comp_str_umap; + +#pragma db value +struct cont_comp1 +{ + // This composite value does not have any columns. + // + num_vector sv; // Have the name "conflic" with the one in the object. +}; + +#pragma db value +struct cont_comp2 +{ + cont_comp2 (): num (777), str ("ggg") {} + + int num; + str_list sl; + std::string str; +}; + +#pragma db object +struct object +{ + object (): nv (comp1_.sv), sl (comp2_.sl) {} + object (const std::string& id) : id_ (id), nv (comp1_.sv), sl (comp2_.sl) {} + + #pragma db id + std::string id_; + + int num; + + cont_comp1 comp1_; + cont_comp2 comp2_; + + // vector + // + #pragma db transient + num_vector& nv; + + #pragma db table("object_strings") id_column ("obj_id") + str_vector sv; + + #pragma db value_column("") + std::vector<comp> cv; + + #pragma db unordered + num_vector uv; + + // list + // + #pragma db transient + str_list& sl; + + // deque + // + num_deque nd; + + // set + // + num_set ns; + str_set ss; + comp_set cs; + + // map + // + num_str_map nsm; + str_num_map snm; + num_comp_map ncm; + comp_str_map csm; + + // array + // + num_array na; + str_array sa; + comp_array ca; + + // forward_list + // + num_flist nfl; + str_flist sfl; + comp_flist cfl; + + // unordered_set + // + num_uset nus; + str_uset sus; + comp_uset cus; + + // unordered_map + // + num_str_umap nsum; + str_num_umap snum; + num_comp_umap ncum; + comp_str_umap csum; + + std::string str; +}; + +inline bool +operator== (const object& x, const object& y) +{ + if (x.uv.size () != y.uv.size ()) + return false; + + int xs (0), ys (0); + + for (num_vector::size_type i (0); i < x.uv.size (); ++i) + { + xs += x.uv[i]; + ys += y.uv[i]; + } + + return + x.id_ == y.id_ && + x.num == y.num && + + x.comp2_.num == y.comp2_.num && + x.comp2_.str == y.comp2_.str && + + x.nv == y.nv && + x.sv == y.sv && + x.cv == y.cv && + xs == ys && + + x.sl == y.sl && + + x.nd == y.nd && + + x.ns == y.ns && + x.ss == y.ss && + x.cs == y.cs && + + x.nsm == y.nsm && + x.snm == y.snm && + x.ncm == y.ncm && + x.csm == y.csm && + + x.na == y.na && + x.sa == y.sa && + x.ca == y.ca && + + x.nfl == y.nfl && + x.sfl == y.sfl && + x.cfl == y.cfl && + + x.nus == y.nus && + x.sus == y.sus && + x.cus == y.cus && + + x.nsum == y.nsum && + x.snum == y.snum && + x.ncum == y.ncum && + x.csum == y.csum && + + x.str == y.str; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/container/basics/testscript b/odb-tests/common/container/basics/testscript new file mode 100644 index 0000000..9e6bfb9 --- /dev/null +++ b/odb-tests/common/container/basics/testscript @@ -0,0 +1,53 @@ +# file : common/container/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/container/change-tracking/buildfile b/odb-tests/common/container/change-tracking/buildfile new file mode 100644 index 0000000..1dda818 --- /dev/null +++ b/odb-tests/common/container/change-tracking/buildfile @@ -0,0 +1,40 @@ +# file : common/container/change-tracking/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_cont_changet_ \ + --generate-schema + +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/container/change-tracking/driver.cxx b/odb-tests/common/container/change-tracking/driver.cxx new file mode 100644 index 0000000..4894ed9 --- /dev/null +++ b/odb-tests/common/container/change-tracking/driver.cxx @@ -0,0 +1,729 @@ +// file : common/container/change-tracking/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test change-tracking containers. +// + +#include <memory> // std::unique_ptr +#include <utility> // std::move +#include <iostream> + +#include <odb/tracer.hxx> +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct counting_tracer: odb::tracer +{ + void + reset (transaction& tr) {u = i = d = s = t = 0; tr.tracer (*this);} + + virtual void + execute (odb::connection&, const char* stmt) + { + string p (stmt, 6); + if (p == "UPDATE") + u++; + else if (p == "INSERT") + i++; + else if (p == "DELETE") + d++; + else if (p == "SELECT") + s++; + t++; + } + + size_t u, i, d, s, t; +}; + +static counting_tracer tr; + +// Compilation test: instantiate all the functions. In C++11 mode only +// do this if we have a fairly conforming compiler that implements the +// complete std::vector interface. +// + +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC +#if defined (__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 7 +struct item {}; +template class odb::vector<item>; +template class odb::vector_iterator<odb::vector<item>, + std::vector<item>::iterator>; +template class odb::vector_iterator<odb::vector<item>, + std::vector<item>::reverse_iterator>; +#endif +#endif + +void +f (const std::vector<int>&) {} + +int +main (int argc, char* argv[]) +{ + try + { + // Test extended interface. + // + { + typedef odb::vector<int> vector; + + vector ov; + std::vector<int> sv; + f (ov); // Implicit conversion to std::vector. + vector ov1 (sv); // Initialization from std::vector. + ov = sv; // Assignement from std::vector. + + // Container comparison. + // + if (ov != ov1 || + ov != sv || + sv != ov1) + ov.clear (); + + // Iterator comparison/conversion. + // + vector::const_iterator i (ov.begin ()); + if (i != ov.end ()) + i = ov.end (); + + // Things are just really borken in Sun CC, no matter which STL + // you use. + // +#ifndef __SUNPRO_CC + vector::const_reverse_iterator j (ov.rbegin ()); + if (j != ov.rend ()) + j = ov.rend (); +#endif + } + + unique_ptr<database> db (create_database (argc, argv)); + + // Test traits logic. + // + { + object o ("1"); + o.i = 123; + o.s.push_back ("a"); + + assert (!o.s._tracking ()); + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + assert (o.s._tracking ()); + + // load + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> ("1")); + assert (p->s._tracking ()); + t.commit (); + } + + // update + // + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + assert (o.s._tracking ()); + + // erase + // + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + + assert (!o.s._tracking ()); + } + + // Test change tracking. + // + object o ("1"); + o.i = 123; + o.s.push_back ("a"); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // push_back/pop_back + // + { + o.s.push_back ("b"); // insert + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.i == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.pop_back (); + o.s.push_back ("c"); // update + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 2 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.pop_back (); + for (int i (0); i != 1024; ++i) + o.s.push_back ("x"); // realloc + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 2 && tr.i == 1023 && tr.t == 1025); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + for (int i (0); i != 1024; ++i) + o.s.pop_back (); // delete + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.d == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.push_back ("b"); + o.s.pop_back (); // no-op + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.t == 1); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // insert + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.insert (o.s.begin (), "a1"); // insert front + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 3 && tr.i == 1 && tr.t == 4); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.insert (o.s.begin () + 1, "a2"); // insert middle + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 3 && tr.i == 1 && tr.t == 4); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.insert (o.s.end (), "b1"); // insert back + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.i == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // erase + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + o.s.push_back ("d"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.erase (o.s.begin ()); // erase front + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 4 && tr.d == 1 && tr.t == 5); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.erase (o.s.begin () + 1); // erase middle + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 2 && tr.d == 1 && tr.t == 3); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.erase (o.s.end () - 1); // erase back + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.d == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // modify + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + o.s.push_back ("d"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.modify (1) += 'b'; + o.s.modify_at (2) += 'c'; + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 3 && tr.t == 3); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.modify_front () += 'a'; + o.s.modify_back () += 'd'; + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 3 && tr.t == 3); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.begin ().modify () += 'a'; +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + o.s.rbegin ().modify () += 'c'; +#endif + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + assert (tr.u == 3 && tr.t == 3); +#else + assert (tr.u == 2 && tr.t == 2); +#endif + assert (*db->load<object> ("1") == o); + t.commit (); + } + +#ifndef _RWSTD_NO_CLASS_PARTIAL_SPEC + { + (o.s.rbegin () + 1).modify (1) += 'a'; + (o.s.rbegin () + 1).modify (-1) += 'c'; + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 3 && tr.t == 3); + assert (*db->load<object> ("1") == o); + t.commit (); + } +#endif + + { + o.s.mbegin (); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 5 && tr.t == 5); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // clear + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.clear (); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.d == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // assign + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.assign (4, "x"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 4 && tr.i == 1 && tr.t == 5); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // resize + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.pop_back (); + o.s.resize (4, "x"); // expand + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 2 && tr.i == 1 && tr.t == 3); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + o.s.push_back ("y"); + o.s.resize (3); // shrink + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.d == 1 && tr.t == 2); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + // Transaction rollback. + // + { + o.s.clear (); + o.s.push_back ("a"); + o.s.push_back ("b"); + o.s.push_back ("c"); + + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (*db->load<object> ("1") == o); + t.commit (); + } + + { + { + o.s.push_back ("d"); + + transaction t (db->begin ()); + db->update (o); + t.rollback (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6); + t.commit (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (o); + assert (tr.u == 1 && tr.t == 1); + t.commit (); + } + } + + // Armed copy. + // + { + unique_ptr<object> c; + + { + o.s.pop_back (); + + transaction t (db->begin ()); + db->update (o); + c.reset (new object (o)); + t.rollback (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.i == 3 && tr.d == 1 && tr.t == 5); + t.commit (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.t == 1); + t.commit (); + } + } + + // Armed swap. + // + { + object c (o); + + { + o.s.push_back ("d"); + + transaction t (db->begin ()); + db->update (o); + assert (o.s._tracking () && !c.s._tracking ()); + c.s.swap (o.s); + assert (!o.s._tracking () && c.s._tracking ()); + t.rollback (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.i == 4 && tr.d == 1 && tr.t == 6); + t.commit (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.t == 1); + t.commit (); + } + } + + // Armed move. + // + { + unique_ptr<object> c; + + { + o.s.pop_back (); + + transaction t (db->begin ()); + db->update (o); + assert (o.s._tracking ()); + c.reset (new object (std::move (o))); + assert (!o.s._tracking () && c->s._tracking ()); + t.rollback (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.i == 2 && tr.d == 1 && tr.t == 4); + t.commit (); + } + + { + transaction t (db->begin ()); + tr.reset (t); + db->update (c); + assert (tr.u == 1 && tr.t == 1); + t.commit (); + } + } + + // Test mixing "smart" and "dumb" container (specifically, erase(obj)). + // + { + mix_object o (1); + o.ov.assign (3, 123); + o.sv.assign (3, 123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test using change tracking container as inverse member. + // + { + inv_object1 o1; + inv_object2 o2; + o1.o2 = &o2; + + { + transaction t (db->begin ()); + db->persist (o2); + db->persist (o1); + t.commit (); + } + + assert (!o2.o1._tracking ()); + + { + session s; + transaction t (db->begin ()); + unique_ptr<inv_object1> p1 (db->load<inv_object1> (o1.id_)); + unique_ptr<inv_object2> p2 (db->load<inv_object2> (o2.id_)); + assert (p2->o1[0] == p1.get ()); + assert (!p2->o1._tracking ()); + t.commit (); + } + } + + // Test read-only values. + { + ro_object o (1); + o.v.push_back (ro_value (1, 1)); + o.v.push_back (ro_value (2, 2)); + o.v.push_back (ro_value (3, 3)); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.v.erase (o.v.begin ()); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (db->load<ro_object> (1)->v == o.v); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/container/change-tracking/test.hxx b/odb-tests/common/container/change-tracking/test.hxx new file mode 100644 index 0000000..8e06f4a --- /dev/null +++ b/odb-tests/common/container/change-tracking/test.hxx @@ -0,0 +1,106 @@ +// file : common/container/change-tracking/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <memory> +#include <vector> +#include <utility> // std::move + +#include <odb/core.hxx> +#include <odb/vector.hxx> + +#pragma db object pointer(std::unique_ptr) +struct object +{ + object () {} + object (const std::string& id): id_ (id) {} + + object (const object& x): id_ (x.id_), i (x.i), s (x.s) {} + object (object&& x): id_ (std::move (x.id_)), i (x.i), s (std::move (x.s)) {} + + #pragma db id + std::string id_; + + unsigned int i; + + odb::vector<std::string> s; + + inline bool + operator== (const object& o) const {return id_ == o.id_ && i == o.i && s == o.s;} +}; + +// Test mixing "smart" and "dumb" container (specifically, erase(obj)). +// +#pragma db object +struct mix_object +{ + mix_object () {} + mix_object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<int> ov; + std::vector<int> sv; +}; + +// Test using change tracking container as inverse member. +// +struct inv_object2; + +#pragma db object session +struct inv_object1 +{ + #pragma db id auto + unsigned long id_; + + inv_object2* o2; +}; + +#pragma db object session +struct inv_object2 +{ + #pragma db id auto + unsigned long id_; + + #pragma db inverse(o2) + odb::vector<inv_object1*> o1; +}; + +// Test read-only values (we still need to include them in the UPDATE +// statement). +// +#pragma db value +struct ro_value +{ + ro_value (int i_ = 0, int j_ = 0): i (i_), j (j_) {} + + #pragma db readonly + int i; + + #pragma db readonly + int j; +}; + +inline bool +operator== (const ro_value& x, const ro_value& y) +{ + return x.i == y.i && x.j == y.j; +} + +#pragma db object +struct ro_object +{ + ro_object () {} + ro_object (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<ro_value> v; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/container/change-tracking/testscript b/odb-tests/common/container/change-tracking/testscript new file mode 100644 index 0000000..d690a30 --- /dev/null +++ b/odb-tests/common/container/change-tracking/testscript @@ -0,0 +1,53 @@ +# file : common/container/change-tracking/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/ctor/buildfile b/odb-tests/common/ctor/buildfile new file mode 100644 index 0000000..a9892bc --- /dev/null +++ b/odb-tests/common/ctor/buildfile @@ -0,0 +1,41 @@ +# file : common/ctor/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 ctor_ \ + --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/ctor/driver.cxx b/odb-tests/common/ctor/driver.cxx new file mode 100644 index 0000000..c9b445d --- /dev/null +++ b/odb-tests/common/ctor/driver.cxx @@ -0,0 +1,79 @@ +// file : common/ctor/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test support for persistent objects without default constructors. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + typedef odb::query<person> query; + typedef odb::result<person> result; + + unique_ptr<database> db (create_database (argc, argv)); + + person p1 ("John", "Doe", 30); + person p2 ("Jane", "Doe", 29); + person p3 ("Joe", "Dirt", 31); + + { + transaction t (db->begin ()); + + db->persist (p1); + db->persist (p2); + db->persist (p3); + + t.commit (); + } + + { + person p ("", "", 0); + + transaction t (db->begin ()); + + db->load (p1.id_, p); + + assert (p.first_ == p1.first_); + assert (p.last_ == p1.last_); + assert (p.age_ == p1.age_); + + result r (db->query<person> (query::age < 30)); + + assert (!r.empty ()); + + result::iterator i (r.begin ()); + i.load (p); + assert (p.first_ == "Jane"); + assert (p.last_ == "Doe"); + assert (p.age_ == 29); + + assert (size (r) == 1); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/ctor/test.hxx b/odb-tests/common/ctor/test.hxx new file mode 100644 index 0000000..2a2becd --- /dev/null +++ b/odb-tests/common/ctor/test.hxx @@ -0,0 +1,29 @@ +// file : common/ctor/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object +struct person +{ + person (const std::string& first, + const std::string& last, + unsigned short age) + : first_ (first), last_ (last), age_ (age) + { + } + + #pragma db id auto + unsigned long id_; + + std::string first_; + std::string last_; + unsigned short age_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/ctor/testscript b/odb-tests/common/ctor/testscript new file mode 100644 index 0000000..60bf027 --- /dev/null +++ b/odb-tests/common/ctor/testscript @@ -0,0 +1,53 @@ +# file : common/ctor/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/default/buildfile b/odb-tests/common/default/buildfile new file mode 100644 index 0000000..e25bd08 --- /dev/null +++ b/odb-tests/common/default/buildfile @@ -0,0 +1,41 @@ +# file : common/default/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 default_ \ + --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/default/driver.cxx b/odb-tests/common/default/driver.cxx new file mode 100644 index 0000000..2d3ef01 --- /dev/null +++ b/odb-tests/common/default/driver.cxx @@ -0,0 +1,81 @@ +// file : common/default/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test default values. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Insert an object using an ad-hoc SQL statement. This way + // we get all the default values. + // + { + transaction t (db->begin ()); + + if (db->id () != odb::id_oracle) + db->execute ("INSERT INTO default_object (obj_id) VALUES (1)"); + else + db->execute ("INSERT INTO \"default_object\" (\"obj_id\") VALUES (1)"); + + t.commit (); + } + + // Now load the object and check all the values. + // + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + t.commit (); + + assert (o->b); + assert (o->pi == 1234); + assert (o->ni == -1234); + assert (o->zi == 0); + assert (o->pf == 1.234); + assert (o->nf == -1.234); + assert (o->zf == 0.0); + assert (o->sf == 1.123e+10); + assert (o->str == "Someone's string"); + assert (o->e == green); + } + + // Check the NULL default value using a query. + // + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::null.is_null ())); + assert (!r.empty ()); + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/default/test.hxx b/odb-tests/common/default/test.hxx new file mode 100644 index 0000000..7f35ed4 --- /dev/null +++ b/odb-tests/common/default/test.hxx @@ -0,0 +1,67 @@ +// file : common/default/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <odb/core.hxx> + +enum color {red, green, blue}; + +#pragma db value(unsigned long) default(0) + +#pragma db object +struct object +{ + #pragma db id + unsigned long obj_id; + + // NULL. + // + #pragma db null default(null) + unsigned long null; + + // Boolean. + // + #pragma db default(true) + bool b; + + // Integers. + // + #pragma db default(1234) + unsigned long pi; + + #pragma db default(-1234) + long ni; + + // 0 default taken from the type. + unsigned long zi; + + // Floats. + // + #pragma db default(1.234) + double pf; + + #pragma db default(-1.234) + double nf; + + #pragma db default(0.0) + double zf; + + #pragma db default(1.123e+10) + double sf; + + // Strings. MySQL doesn't support default values on TEXT + // columns, so make the type VARCHAR. + // + #pragma db type("VARCHAR(64)") default("Someone's string") + std::string str; + + // Enums. + // + #pragma db default(green) + color e; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/default/testscript b/odb-tests/common/default/testscript new file mode 100644 index 0000000..47ab5ec --- /dev/null +++ b/odb-tests/common/default/testscript @@ -0,0 +1,53 @@ +# file : common/default/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/definition/.gitignore b/odb-tests/common/definition/.gitignore new file mode 100644 index 0000000..5838670 --- /dev/null +++ b/odb-tests/common/definition/.gitignore @@ -0,0 +1,6 @@ +# ODB-generated files. +# +time-mapping-odb.?xx +time-mapping-odb-*.?xx +time-mapping.sql +time-mapping-*.sql diff --git a/odb-tests/common/definition/buildfile b/odb-tests/common/definition/buildfile new file mode 100644 index 0000000..83a09ff --- /dev/null +++ b/odb-tests/common/definition/buildfile @@ -0,0 +1,52 @@ +# file : common/definition/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = test time-mapping + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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 definition_ \ + --generate-schema + +<{hxx ixx cxx}{time-mapping-odb}>: odb_options = + +for db: $databases + {hxx ixx cxx}{time-mapping-odb-$db}: odb_options = + +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/definition/driver.cxx b/odb-tests/common/definition/driver.cxx new file mode 100644 index 0000000..223eeaf --- /dev/null +++ b/odb-tests/common/definition/driver.cxx @@ -0,0 +1,56 @@ +// file : common/definition/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test overriding composite value definition point. This is primarily +// useful to make composite values out of third-party types. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + object o; + o.time.tv_sec = 1; + o.time.tv_usec = 1000; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id)); + t.commit (); + + assert (p->time.tv_sec == o.time.tv_sec && + p->time.tv_usec == o.time.tv_usec); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/definition/test.hxx b/odb-tests/common/definition/test.hxx new file mode 100644 index 0000000..38fc02a --- /dev/null +++ b/odb-tests/common/definition/test.hxx @@ -0,0 +1,26 @@ +// file : common/definition/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#ifdef _WIN32 +# include <winsock2.h> // timeval +#else +# include <sys/time.h> // timeval +#endif + +#include <odb/core.hxx> + +#include "time-mapping.hxx" + +#pragma db object +struct object +{ + #pragma db id auto + unsigned long id; + + timeval time; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/definition/testscript b/odb-tests/common/definition/testscript new file mode 100644 index 0000000..5d59884 --- /dev/null +++ b/odb-tests/common/definition/testscript @@ -0,0 +1,53 @@ +# file : common/definition/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/definition/time-mapping.hxx b/odb-tests/common/definition/time-mapping.hxx new file mode 100644 index 0000000..469cfb7 --- /dev/null +++ b/odb-tests/common/definition/time-mapping.hxx @@ -0,0 +1,17 @@ +// file : common/definition/time-mapping.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TIME_MAPPING_HXX +#define TIME_MAPPING_HXX + +#ifdef _WIN32 +# include <winsock2.h> // timeval +#else +# include <sys/time.h> // timeval +#endif + +#pragma db value(timeval) definition +#pragma db member(timeval::tv_sec) column("sec") +#pragma db member(timeval::tv_usec) column("usec") + +#endif // TIME_MAPPING_HXX diff --git a/odb-tests/common/enum/buildfile b/odb-tests/common/enum/buildfile new file mode 100644 index 0000000..eb3a29a --- /dev/null +++ b/odb-tests/common/enum/buildfile @@ -0,0 +1,41 @@ +# file : common/enum/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 enum_ \ + --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/enum/driver.cxx b/odb-tests/common/enum/driver.cxx new file mode 100644 index 0000000..ed3eb59 --- /dev/null +++ b/odb-tests/common/enum/driver.cxx @@ -0,0 +1,84 @@ +// file : common/enum/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test automatic C++ enum mapping. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + unique_ptr<database> db (create_database (argc, argv)); + + object o; + o.color_ = green; + o.taste_ = object::sweet; + o.position_ = object::left; + + o.gender_ = object::gender::female; + o.scale_ = object::scale::ten; + o.yesno_ = object::yesno::yes; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (o.id_)); + t.commit (); + + assert (o == *o1); + } + + { + transaction t (db->begin ()); + + result r1 (db->query<object> (query::color == blue)); + result r2 (db->query<object> (query::taste == object::sweet)); + result r3 (db->query<object> (query::position == object::left)); + + assert (r1.empty ()); + assert (!r2.empty ()); + assert (!r3.empty ()); + + result r4 (db->query<object> (query::gender == object::gender::female)); + result r5 (db->query<object> (query::scale == object::scale::ten)); + result r6 (db->query<object> (query::yesno == object::yesno::yes)); + + assert (!r4.empty ()); + assert (!r5.empty ()); + assert (!r6.empty ()); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/enum/test.hxx b/odb-tests/common/enum/test.hxx new file mode 100644 index 0000000..a279112 --- /dev/null +++ b/odb-tests/common/enum/test.hxx @@ -0,0 +1,47 @@ +// file : common/enum/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <odb/core.hxx> + +enum color {red, green, blue}; + +#pragma db object +struct object +{ + #pragma db id auto + unsigned long id_; + + color color_; + enum taste {bitter, sweet, sour}; + taste taste_; + + enum position {left = -1, center = 0, right = 1}; + position position_; + + + enum class gender {male, female}; + enum class scale: unsigned char {one = 1, ten = 10, hundred = 100}; + enum class yesno: bool {no, yes}; + + gender gender_; + scale scale_; + yesno yesno_; +}; + +inline bool +operator == (const object& x, const object& y) +{ + return + x.id_ == y.id_ + && x.color_ == y.color_ + && x.taste_ == y.taste_ + && x.position_ == y.position_ + && x.gender_ == y.gender_ + && x.scale_ == y.scale_ + && x.yesno_ == y.yesno_; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/enum/testscript b/odb-tests/common/enum/testscript new file mode 100644 index 0000000..fbf1a99 --- /dev/null +++ b/odb-tests/common/enum/testscript @@ -0,0 +1,53 @@ +# file : common/enum/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/erase-query/buildfile b/odb-tests/common/erase-query/buildfile new file mode 100644 index 0000000..d833b6e --- /dev/null +++ b/odb-tests/common/erase-query/buildfile @@ -0,0 +1,41 @@ +# file : common/erase-query/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 erase_query_ \ + --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/erase-query/driver.cxx b/odb-tests/common/erase-query/driver.cxx new file mode 100644 index 0000000..6c11957 --- /dev/null +++ b/odb-tests/common/erase-query/driver.cxx @@ -0,0 +1,181 @@ +// file : common/erase-query/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test query-based erase. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +void +persist (database& db) +{ + object o1 (1); + object o2 (2); + object o3 (3); + object o4 (4); + + transaction t (db.begin ()); + db.persist (o1); + db.persist (o2); + db.persist (o3); + db.persist (o4); + t.commit (); +} + +int +main (int argc, char* argv[]) +{ + try + { + typedef odb::query<object> query; + + unique_ptr<database> db (create_database (argc, argv)); + + // erase_query() + // + persist (*db); + + { + transaction t (db->begin ()); + assert (db->erase_query<object> () == 4); + t.commit (); + } + + // erase_query(const char*) + // + persist (*db); + + { + transaction t (db->begin ()); + + if (db->id () != odb::id_oracle) + assert (db->erase_query<object> ( + "erase_query_object.id < 3") == 2); + else + assert (db->erase_query<object> ( + "\"erase_query_object\".\"id\" < 3") == 2); + + db->erase_query<object> (); + t.commit (); + } + + // erase_query(query) + // + persist (*db); + + { + transaction t (db->begin ()); + assert (db->erase_query<object> (query::id == 2 || query::id == 4) == 2); + db->erase_query<object> (); + t.commit (); + } + + // Test predicates involving object pointers (DELETE JOIN). + // + /* + { + object o11 (1); + object o12 (2); + object o13 (3); + object2 o2; + + o11.o2 = &o2; + o2.num = 123; + + o12.o1 = &o13; + o13.num = 123; + + transaction t (db->begin ()); + db->persist (o2); + db->persist (o13); + db->persist (o12); + db->persist (o11); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (db->erase_query<object> (query::o1::num == 123) == 1); + assert (db->erase_query<object> (query::o2::num == 123) == 1); + db->erase_query<object> (); + t.commit (); + } + */ + + // For now we can only do column-based tests, like is_null(). + // + { + object o11 (1); + object o12 (2); + object o13 (3); + object2 o2; + + o12.o2 = &o2; + + transaction t (db->begin ()); + db->persist (o2); + db->persist (o13); + db->persist (o12); + db->persist (o11); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (db->erase_query<object> (query::o2.is_null ()) == 2); + db->erase_query<object> (); + t.commit (); + } + + // Make sure container data is deleted. + // + { + object o (1); + o.v.push_back (1); + o.v.push_back (2); + o.v.push_back (3); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (db->erase_query<object> () == 1); + t.commit (); + } + + { + transaction t (db->begin ()); + + if (db->id () != odb::id_oracle) + assert (db->execute ("SELECT * FROM erase_query_object_v " + "WHERE object_id = 1") == 0); + else + assert (db->execute ("SELECT * FROM \"erase_query_object_v\" " + "WHERE \"object_id\" = 1") == 0); + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/erase-query/test.hxx b/odb-tests/common/erase-query/test.hxx new file mode 100644 index 0000000..9e73f12 --- /dev/null +++ b/odb-tests/common/erase-query/test.hxx @@ -0,0 +1,46 @@ +// file : common/erase-query/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> + +#include <odb/core.hxx> + +struct object2; + +#pragma db object +struct object +{ + object (unsigned long id) + : id_ (id), o1 (0), o2 (0) + { + } + + object () + : o1 (0), o2 (0) + { + } + + #pragma db id + unsigned long id_; + + std::vector<int> v; + + int num; + + object* o1; + object2* o2; +}; + +#pragma db object +struct object2 +{ + #pragma db id auto + unsigned long id_; + + int num; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/erase-query/testscript b/odb-tests/common/erase-query/testscript new file mode 100644 index 0000000..bdb7fac --- /dev/null +++ b/odb-tests/common/erase-query/testscript @@ -0,0 +1,53 @@ +# file : common/erase-query/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/id/auto/buildfile b/odb-tests/common/id/auto/buildfile new file mode 100644 index 0000000..c340200 --- /dev/null +++ b/odb-tests/common/id/auto/buildfile @@ -0,0 +1,40 @@ +# file : common/id/auto/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_id_auto_ \ + --generate-schema + +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/id/auto/driver.cxx b/odb-tests/common/id/auto/driver.cxx new file mode 100644 index 0000000..d294e69 --- /dev/null +++ b/odb-tests/common/id/auto/driver.cxx @@ -0,0 +1,96 @@ +// file : common/id/auto/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test automatic id assignment. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // object + // + { + unsigned long id1, id2, id3; + { + object o1 ("one"); + object o2 ("two"); + object o3 ("three"); + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + t.commit (); + + id1 = o1.id_; + id2 = o2.id_; + id3 = o3.id_; + + assert (id1 != id2); + assert (id1 != id3); + assert (id2 != id3); + } + + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (id1)); + unique_ptr<object> o2 (db->load<object> (id2)); + unique_ptr<object> o3 (db->load<object> (id3)); + t.commit (); + + assert (o1->id_ == id1 && o1->str_ == "one"); + assert (o2->id_ == id2 && o2->str_ == "two"); + assert (o3->id_ == id3 && o3->str_ == "three"); + } + } + + // auto_only + // + { + unsigned short id; + { + auto_only o; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + id = o.id_; + } + + { + transaction t (db->begin ()); + unique_ptr<auto_only> o (db->load<auto_only> (id)); + t.commit (); + + assert (o->id_ == id); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/id/auto/test.hxx b/odb-tests/common/id/auto/test.hxx new file mode 100644 index 0000000..233c79f --- /dev/null +++ b/odb-tests/common/id/auto/test.hxx @@ -0,0 +1,40 @@ +// file : common/id/auto/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object +struct object +{ + object (const std::string& str) + : id_ (1), str_ (str) + { + } + + #pragma db auto id + unsigned long id_; + std::string str_; + +private: + object () + { + } + + friend class odb::access; +}; + +// Test the case where the object has just the auto id. +// +#pragma db object +struct auto_only +{ + #pragma db auto id pgsql:type("BIGINT") + unsigned short id_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/id/auto/testscript b/odb-tests/common/id/auto/testscript new file mode 100644 index 0000000..641f03d --- /dev/null +++ b/odb-tests/common/id/auto/testscript @@ -0,0 +1,53 @@ +# file : common/id/auto/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/id/composite/buildfile b/odb-tests/common/id/composite/buildfile new file mode 100644 index 0000000..4bc9f9a --- /dev/null +++ b/odb-tests/common/id/composite/buildfile @@ -0,0 +1,42 @@ +# file : common/id/composite/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_id_comp_ \ + --generate-schema \ + --generate-query \ + --generate-session + +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/id/composite/driver.cxx b/odb-tests/common/id/composite/driver.cxx new file mode 100644 index 0000000..3d66101 --- /dev/null +++ b/odb-tests/common/id/composite/driver.cxx @@ -0,0 +1,731 @@ +// file : common/id/composite/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test composite object ids. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test 1. + // + { + using namespace test1; + + object o1 (scomp ("aaa", "bbb", "ccc"), 123); + o1.vec.push_back (scomp ("xxx", "xxx", "xxx")); + o1.vec.push_back (scomp ("yyy", "yyy", "yyy")); + + object o2 (scomp ("aaa", "bbb", "ccd"), 234); + o2.vec.push_back (scomp ("zzz", "", "zzz")); + + object o3 (scomp ("baa", "bbb", "ccc"), 345); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + unique_ptr<object> p3 (db->load<object> (o3.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p3 == o3); + } + + // Update. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o1.id)); + p->num++; + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o1.id)); + t.commit (); + + assert (p->num == o1.num + 1); + } + + // Erase. + // + { + transaction t (db->begin ()); + db->erase<object> (o1.id); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->find<object> (o1.id)); + assert (p.get () == 0); + t.commit (); + } + } + + // Test 2. + // + { + using namespace test2; + + object2 o2 (ncomp (2, 0, 1)); + o2.o1 = new object1 (scomp ("o1", "o2", "aaa")); + + object3 o3 (ncomp (3, 0, 1)); + o3.o1.push_back (new object1 (scomp ("o1", "o3", "aaa"))); + o3.o1.push_back (new object1 (scomp ("o1", "o3", "bbb"))); + + object4 o4 (ncomp (4, 0, 1)); + o4.c.o2 = new object2 (ncomp (2, 4, 1)); + o4.c.o2->o1 = new object1 (scomp ("o1", "o2", "ccc")); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2.o1); + db->persist (o2); + db->persist (o3.o1[0]); + db->persist (o3.o1[1]); + db->persist (o3); + db->persist (o4.c.o2->o1); + db->persist (o4.c.o2); + db->persist (o4); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + unique_ptr<object3> p3 (db->load<object3> (o3.id)); + unique_ptr<object4> p4 (db->load<object4> (o4.id)); + t.commit (); + + assert (p2->o1->id == o2.o1->id); + assert (p3->o1.size () == o3.o1.size ()); + assert (p3->o1[0]->id == o3.o1[0]->id); + assert (p3->o1[1]->id == o3.o1[1]->id); + assert (p4->c.o2->id == o4.c.o2->id); + assert (p4->c.o2->o1->id == o4.c.o2->o1->id); + } + + // Update. + // + { + scomp id2, id3; + + { + transaction t (db->begin ()); + + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + delete p2->o1; + p2->o1 = new object1 (scomp ("o1", "o2", "bbb")); + id2 = db->persist (p2->o1); + db->update (*p2); + + unique_ptr<object3> p3 (db->load<object3> (o3.id)); + delete p3->o1.back (); + p3->o1.pop_back (); + p3->o1.push_back (new object1 (scomp ("o1", "o3", "ccc"))); + id3 = db->persist (p3->o1.back ()); + db->update (*p3); + + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + unique_ptr<object3> p3 (db->load<object3> (o3.id)); + t.commit (); + + assert (p2->o1->id == id2); + assert (p3->o1.back ()->id == id3); + } + } + + // Query. + // + { + { + typedef odb::query<object2> query; + typedef odb::result<object2> result; + + transaction t (db->begin ()); + + { + result r (db->query<object2> (query::o1->id.str3 == "bbb")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id == o2.id); + assert (++i == r.end ()); + } + + { + // As id (dual interface). + // + result r (db->query<object2> (query::o1.str3 == "bbb")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id == o2.id); + assert (++i == r.end ()); + } + + t.commit (); + } + + // Second level composite object pointer. + // + { + typedef odb::query<object4> query; + typedef odb::result<object4> result; + + transaction t (db->begin ()); + + result r (db->query<object4> (query::c.o2->o1.str3 == "ccc")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id == o4.id); + assert (++i == r.end ()); + + t.commit (); + } + } + + // View. + // + { + transaction t (db->begin ()); + + { + typedef odb::query<view2> query; + typedef odb::result<view2> result; + + result r (db->query<view2> (query::object2::id.num2 == 0)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "bbb"); + assert (++i == r.end ()); + } + + { + typedef odb::query<view3> query; + typedef odb::result<view3> result; + + result r (db->query<view3> ((query::object3::id.num2 == 0) + + "ORDER BY" + query::object1::id.str3)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "aaa"); + assert (++i != r.end ()); + assert (i->num == 1 && i->str == "ccc"); + assert (++i == r.end ()); + } + + { + typedef odb::query<view4> query; + typedef odb::result<view4> result; + + result r (db->query<view4> (query::object4::id.num2 == 0)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num4 == 1 && i->num2 == 1 && i->str == "ccc"); + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // Test 3. + // + { + using namespace test3; + + object2 o2 (ncomp (2, 0, 1)); + o2.o1 = new object1 (scomp ("o1", "o2", "aaa")); + o2.o1->o2 = &o2; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2.o1); + db->persist (o2); + t.commit (); + } + + // Load. + // + { + session s; + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + t.commit (); + + assert (p2->o1->o2->id == o2.id); + } + + // Query. + // + { + typedef odb::query<object1> query; + typedef odb::result<object1> result; + + transaction t (db->begin ()); + + { + session s; + + result r (db->query<object1> (query::o2->id.num2 == 0)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id == o2.o1->id); + + i->o2->o1 = 0; + delete i->o2; + + assert (++i == r.end ()); + } + + t.commit (); + } + + // View. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + + result r (db->query<view> (query::object1::id.str2 == "o2")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "aaa"); + assert (++i == r.end ()); + + t.commit (); + } + } + + // Test 4. + // + { + using namespace test4; + + object2 o2 (ncomp (2, 0, 1)); + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa"))); + o2.o1.back ()->o2 = &o2; + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb"))); + o2.o1.back ()->o2 = &o2; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2.o1[0]); + db->persist (o2.o1[1]); + db->persist (o2); + t.commit (); + } + + // Load. + // + { + session s; + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + t.commit (); + + assert (p2->o1.size () == 2); + assert (p2->o1[0]->o2->id == o2.id); + assert (p2->o1[1]->o2->id == o2.id); + } + + // Query. + // + { + typedef odb::query<object1> query; + typedef odb::result<object1> result; + + transaction t (db->begin ()); + + { + session s; + + result r (db->query<object1> (query::o2->id.num2 == 0)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id == o2.o1[0]->id); + i->o2->o1.clear (); + + assert (++i != r.end ()); + assert (i->id == o2.o1[1]->id); + + i->o2->o1.clear (); + delete i->o2; + + assert (++i == r.end ()); + } + + t.commit (); + } + + // View. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + + result r (db->query<view> (query::object1::id.str3 == "bbb")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "bbb"); + assert (++i == r.end ()); + + t.commit (); + } + } + + // Test 5. + // + { + using namespace test5; + + object2 o2 (ncomp (2, 0, 1)); + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa"))); + o2.o1.back ()->o2 = &o2; + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb"))); + o2.o1.back ()->o2 = &o2; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2.o1[0]); + db->persist (o2.o1[1]); + db->persist (o2); + t.commit (); + } + + // Load. + // + { + session s; + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + t.commit (); + + assert (p2->o1.size () == 2); + + assert (p2->o1[0]->id == o2.o1[0]->id); + assert (p2->o1[0]->o2->id == o2.id); + + assert (p2->o1[1]->id == o2.o1[1]->id); + assert (p2->o1[1]->o2->id == o2.id); + } + + // View. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + + result r (db->query<view> ((query::object2::id.num2 == 0) + + "ORDER BY" + query::object1::id.str3)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "aaa"); + assert (++i != r.end ()); + assert (i->num == 1 && i->str == "bbb"); + assert (++i == r.end ()); + + t.commit (); + } + } + + // Test 6. + // + { + using namespace test6; + + object2 o2 (ncomp (2, 0, 1)); + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "aaa"))); + o2.o1.back ()->o2.push_back (&o2); + + o2.o1.push_back (new object1 (scomp ("o1", "o2", "bbb"))); + o2.o1.back ()->o2.push_back (&o2); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2.o1[0]); + db->persist (o2.o1[1]); + db->persist (o2); + t.commit (); + } + + // Load. + // + { + session s; + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> (o2.id)); + t.commit (); + + assert (p2->o1.size () == 2); + + assert (p2->o1[0]->id == o2.o1[0]->id); + assert (p2->o1[0]->o2[0]->id == o2.id); + + assert (p2->o1[1]->id == o2.o1[1]->id); + assert (p2->o1[1]->o2[0]->id == o2.id); + } + + // View. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + + result r (db->query<view> ((query::object2::id.num2 == 0) + + "ORDER BY" + query::object1::id.str3)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "aaa"); + assert (++i != r.end ()); + assert (i->num == 1 && i->str == "bbb"); + assert (++i == r.end ()); + + t.commit (); + } + } + + // Test 7. + // + { + using namespace test7; + + object o (scomp ("aaa", "bbb", "ccc"), 123); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id)); + t.commit (); + + assert (*p == o); + } + + // Update. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id)); + p->num++; + db->update (*p); + + try + { + db->update (o); + assert (false); + } + catch (const object_changed&) + { + } + + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id)); + t.commit (); + + assert (p->num == o.num + 1); + } + + // Erase. + // + { + transaction t (db->begin ()); + + try + { + db->update (o); + assert (false); + } + catch (const object_changed&) + { + } + + t.commit (); + } + } + + // Test 8. + // + { + using namespace test8; + + object2 o2a, o2b; + object3 o3; + + o2b.o1 = new object1 (scomp ("222", "aaa", "bbb"), 123); + o3.o1.push_back (0); + o3.o1.push_back (new object1 (scomp ("333", "aaa", "bbb"), 234)); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o2a); + db->persist (o2b); + db->persist (o2b.o1); + db->persist (o3); + db->persist (o3.o1[1]); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object2> p2a (db->load<object2> (o2a.id)); + unique_ptr<object2> p2b (db->load<object2> (o2b.id)); + unique_ptr<object3> p3 (db->load<object3> (o3.id)); + t.commit (); + + assert (p2a->o1 == 0); + assert (p2b->o1 != 0 && *p2b->o1 == *o2b.o1); + assert (p3->o1[0] == 0); + assert (p3->o1[1] != 0 && *p3->o1[1] == *o3.o1[1]); + } + + // Update. + // + { + object1* o1 (o3.o1[1]); + + o3.o1.clear (); + o3.o1.push_back (o2b.o1); + o3.o1.push_back (0); + + o2a.o1 = o1; + o2b.o1 = 0; + + transaction t (db->begin ()); + db->update (o2a); + db->update (o2b); + db->update (o3); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> p2a (db->load<object2> (o2a.id)); + unique_ptr<object2> p2b (db->load<object2> (o2b.id)); + unique_ptr<object3> p3 (db->load<object3> (o3.id)); + t.commit (); + + assert (p2a->o1 != 0 && *p2a->o1 == *o2a.o1); + assert (p2b->o1 == 0); + assert (p3->o1[0] != 0 && *p3->o1[0] == *o3.o1[0]); + assert (p3->o1[1] == 0); + } + } + + // Test 9. + { + using namespace test9; + + object o (123, "abc"); + o.v.push_back (123); + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + result<object> r (db->query<object> ()); + result<object>::iterator i (r.begin ()); + assert (i != r.end () && o == *i && ++i == r.end ()); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/id/composite/test.hxx b/odb-tests/common/id/composite/test.hxx new file mode 100644 index 0000000..70856a6 --- /dev/null +++ b/odb-tests/common/id/composite/test.hxx @@ -0,0 +1,519 @@ +// file : common/id/composite/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +#pragma db value +struct scomp +{ + scomp () {} + scomp (const std::string& s1, const std::string& s2, const std::string& s3) + : str1 (s1), str2 (s2), str3 (s3) + { + } + + std::string str1; + std::string str2; + std::string str3; +}; + +inline bool +operator== (const scomp& x, const scomp& y) +{ + return x.str1 == y.str1 && x.str2 == y.str2 && x.str3 == y.str3; +} + +inline bool +operator< (const scomp& x, const scomp& y) +{ + return x.str1 < y.str1 || + (x.str1 == y.str1 && x.str2 < y.str2) || + (x.str1 == y.str1 && x.str2 == y.str2 && x.str3 < y.str3); +} + +#pragma db value +struct ncomp +{ + ncomp () {} + ncomp (unsigned short n1, unsigned short n2, unsigned short n3) + : num1 (n1), num2 (n2), num3 (n3) + { + } + + unsigned short num1; + unsigned short num2; + unsigned short num3; +}; + +inline bool +operator== (const ncomp& x, const ncomp& y) +{ + return x.num1 == y.num1 && x.num2 == y.num2 && x.num3 == y.num3; +} + +inline bool +operator< (const ncomp& x, const ncomp& y) +{ + return x.num1 < y.num1 || + (x.num1 == y.num1 && x.num2 < y.num2) || + (x.num1 == y.num1 && x.num2 == y.num2 && x.num3 < y.num3); +} + +// Test object with composite id, container. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object + struct object + { + object () {} + object (const scomp& i, unsigned long n): id (i), num (n) {} + + #pragma db id + scomp id; + + unsigned long num; + std::vector<scomp> vec; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.id == y.id && x.num == y.num && x.vec == y.vec; + } +} + +// Test to-one and to-many relationships with composite id as well as +// queries and views. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i): id (i) {} + + #pragma db id + scomp id; + }; + + #pragma db object + struct object2 + { + object2 (): o1 (0) {} + object2 (const ncomp& i): id (i), o1 (0) {} + ~object2 () {delete o1;} + + #pragma db id + ncomp id; + + object1* o1; + }; + + #pragma db object + struct object3 + { + object3 () {} + object3 (const ncomp& i): id (i) {} + + ~object3 () + { + for (std::vector<object1*>::iterator i (o1.begin ()); + i != o1.end (); ++i) + delete *i; + } + + #pragma db id + ncomp id; + + std::vector<object1*> o1; + }; + + // Test second-level query pointer test as well as pointers in + // composite types. + // + #pragma db value + struct comp + { + comp (): o2 (0) {} + ~comp () {delete o2;} + + object2* o2; + }; + + #pragma db object + struct object4 + { + object4 () {} + object4 (const ncomp& i): id (i) {} + + #pragma db id + ncomp id; + + comp c; + }; + + #pragma db view object(object2) object(object1) + struct view2 + { + #pragma db column (object2::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; + + #pragma db view object(object3) object(object1) + struct view3 + { + #pragma db column (object3::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; + + #pragma db view object(object4) object(object2) object(object1) + struct view4 + { + #pragma db column (object4::id.num3) + unsigned short num4; + + #pragma db column (object2::id.num3) + unsigned short num2; + + #pragma db column (object1::id.str3) + std::string str; + }; +} + +// Test one-to-one(i) relationship with composite id. +// +#pragma db namespace table("t3_") +namespace test3 +{ + struct object2; + + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i): id (i) {} + + #pragma db id + scomp id; + + #pragma db inverse(o1) + object2* o2; + }; + + #pragma db object + struct object2 + { + object2 (): o1 (0) {} + object2 (const ncomp& i): id (i), o1 (0) {} + ~object2 () {delete o1;} + + #pragma db id + ncomp id; + + object1* o1; + }; + + #pragma db view object(object2) object(object1) + struct view + { + #pragma db column (object2::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; +} + +// Test many-to-one(i) relationship with composite id. +// +#pragma db namespace table("t4_") +namespace test4 +{ + struct object2; + + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i): id (i) {} + + #pragma db id + scomp id; + + #pragma db inverse(o1) + object2* o2; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const ncomp& i): id (i) {} + + ~object2 () + { + for (std::vector<object1*>::iterator i (o1.begin ()); + i != o1.end (); ++i) + delete *i; + } + + #pragma db id + ncomp id; + + std::vector<object1*> o1; + }; + + #pragma db view object(object2) object(object1) + struct view + { + #pragma db column (object2::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; +} + +// Test one-to-many(i) relationship with composite id. +// +#pragma db namespace table("t5_") +namespace test5 +{ + struct object2; + + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i): id (i) {} + + #pragma db id + scomp id; + + object2* o2; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const ncomp& i): id (i) {} + + ~object2 () + { + for (std::vector<object1*>::iterator i (o1.begin ()); + i != o1.end (); ++i) + delete *i; + } + + #pragma db id + ncomp id; + + #pragma db inverse(o2) + std::vector<object1*> o1; + }; + + #pragma db view object(object2) object(object1) + struct view + { + #pragma db column (object2::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; +} + +// Test many-to-many(i) relationship with composite id. +// +#pragma db namespace table("t6_") +namespace test6 +{ + struct object2; + + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i): id (i) {} + + #pragma db id + scomp id; + + std::vector<object2*> o2; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const ncomp& i): id (i) {} + + ~object2 () + { + for (std::vector<object1*>::iterator i (o1.begin ()); + i != o1.end (); ++i) + delete *i; + } + + #pragma db id + ncomp id; + + #pragma db inverse(o2) + std::vector<object1*> o1; + }; + + #pragma db view object(object2) object(object1) + struct view + { + #pragma db column (object2::id.num3) + unsigned short num; + + #pragma db column (object1::id.str3) + std::string str; + }; +} + +// Test object with composite id and version (optimistic concurrency). +// +#pragma db namespace table("t7_") +namespace test7 +{ + #pragma db object optimistic + struct object + { + object () {} + object (const scomp& i, unsigned long n): id (i), num (n) {} + + #pragma db id + scomp id; + + #pragma db version + unsigned long ver; + + unsigned long num; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.id == y.id && x.ver == y.ver && x.num == y.num; + } +} + +// Test composite NULL pointers. +// +#pragma db namespace table("t8_") +namespace test8 +{ + #pragma db object + struct object1 + { + object1 () {} + object1 (const scomp& i, unsigned long n): id (i), num (n) {} + + #pragma db id + scomp id; + + unsigned long num; + }; + + inline bool + operator== (const object1& x, const object1& y) + { + return x.id == y.id && x.num == y.num; + } + + #pragma db object + struct object2 + { + object2 (): o1 (0) {} + ~object2 () {delete o1;} + + #pragma db id auto + unsigned long id; + + object1* o1; + }; + + #pragma db object + struct object3 + { + ~object3 () + { + for (std::vector<object1*>::iterator i (o1.begin ()); + i != o1.end (); ++i) + delete *i; + } + + #pragma db id auto + unsigned long id; + + std::vector<object1*> o1; + }; +} + +// Test composite id definition inside object. +// +#pragma db namespace table("t9_") +namespace test9 +{ + #pragma db object + struct object + { + object (unsigned long n = 0, const std::string& s = "") + { + id_.num = n; + id_.str = s; + } + + unsigned long num () const {return id_.num;} + const std::string& str () const {return id_.str;} + + std::vector<int> v; + + private: + friend class odb::access; + + #pragma db value + struct comp + { + unsigned long num; + std::string str; + + bool + operator< (const comp& x) const + { + return num < x.num || (num == x.num && str < x.str); + } + }; + + #pragma db id + comp id_; + }; + + inline bool + operator== (const object& x, const object& y) + { + return x.num () == y.num () && x.str () == y.str () && x.v == y.v; + } +} + + +#endif // TEST_HXX diff --git a/odb-tests/common/id/composite/testscript b/odb-tests/common/id/composite/testscript new file mode 100644 index 0000000..b74abdb --- /dev/null +++ b/odb-tests/common/id/composite/testscript @@ -0,0 +1,53 @@ +# file : common/id/composite/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/id/nested/buildfile b/odb-tests/common/id/nested/buildfile new file mode 100644 index 0000000..777cb65 --- /dev/null +++ b/odb-tests/common/id/nested/buildfile @@ -0,0 +1,41 @@ +# file : common/nested/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_id_nested_ \ + --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/id/nested/driver.cxx b/odb-tests/common/id/nested/driver.cxx new file mode 100644 index 0000000..92a80f6 --- /dev/null +++ b/odb-tests/common/id/nested/driver.cxx @@ -0,0 +1,266 @@ +// file : common/id/nested/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test nested ids. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + + // Simple nested id. + // + { + using namespace test1; + + object o1 (1, "a", 3); + o1.v.push_back (123); + + object o2 (4, "b", 6); + o2.v.push_back (234); + + object1 o (new object (10, "abc", 11)); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o.p); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + unique_ptr<object1> p (db->load<object1> (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + + o1.z++; + o1.v.pop_back (); + o1.v.push_back (234); + + o2.z--; + o2.v.back ()++; + o2.v.push_back (123); + + delete o.p; + o.p = new object (20, "xyz", 11); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + db->persist (o.p); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + unique_ptr<object1> p (db->load<object1> (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + } + + // Composite nested id. + // + { + using namespace test2; + + object o1 (1, 2, "a", 123); + o1.v.push_back (123); + + object o2 (1, 3, "b", 234); + o2.v.push_back (234); + + object1 o (new object (2, 2, "abc", 123)); + o.p->v.push_back (345); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o.p); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.c)); + unique_ptr<object> p2 (db->load<object> (o2.id.c)); + unique_ptr<object1> p (db->load<object1> (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + + o1.z++; + o1.v.pop_back (); + o1.v.push_back (234); + + o2.z--; + o2.v.modify_back ()++; + o2.v.push_back (123); + + delete o.p; + o.p = new object (2, 3, "xyz", 234); + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + db->persist (o.p); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.c)); + unique_ptr<object> p2 (db->load<object> (o2.id.c)); + unique_ptr<object1> p (db->load<object1> (o.id)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + assert (*p == o); + } + } + + // Custom/by-value access. + // + { + using namespace test3; + + object o1 (1, "a", 3); + object o2 (4, "b", 6); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o1.z++; + o2.z--; + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + + // Polymorphic. + // + { + using namespace test4; + + base o1 (1, "a"); + object o2 (2, "b", 1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p1 (db->load<base> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + + o2.z--; + + { + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p1 (db->load<base> (o1.id.y)); + unique_ptr<object> p2 (db->load<object> (o2.id.y)); + t.commit (); + + assert (*p1 == o1); + assert (*p2 == o2); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/id/nested/test.hxx b/odb-tests/common/id/nested/test.hxx new file mode 100644 index 0000000..06ee6b8 --- /dev/null +++ b/odb-tests/common/id/nested/test.hxx @@ -0,0 +1,217 @@ +// file : common/id/nested/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> +#include <odb/vector.hxx> + +// Simple nested id. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db value + struct comp + { + int x; + std::string y; + }; + + #pragma db object + struct object + { + #pragma db id(y) + comp id; + + int z; + std::vector<int> v; + + object () {} + object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z && a.v == b.v; + } + + #pragma db object + struct object1 + { + #pragma db id auto + int id; + + object* p; + + object1 (object* p_ = 0): p (p_) {} + ~object1 () {delete p;} + }; + + inline bool + operator== (const object1& a, const object1& b) + { + return a.id == b.id && *a.p == *b.p; + } +} + +// Composite nested id. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db value + struct comp1 + { + int x; + int y; + }; + + #pragma db value + struct comp2 + { + comp1 c; + std::string s; + }; + + #pragma db object + struct object + { + #pragma db id(c) + comp2 id; + + int z; + odb::vector<int> v; + + object () {} + object (int x, int y, std::string s, int z_) + : z (z_) {id.c.x = x; id.c.y = y; id.s = s;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.c.x == b.id.c.x && a.id.c.y == b.id.c.y && + a.id.s == b.id.s && a.z == b.z && a.v == b.v; + } + + #pragma db object + struct object1 + { + #pragma db id auto + int id; + + object* p; + + object1 (object* p_ = 0): p (p_) {} + ~object1 () {delete p;} + }; + + inline bool + operator== (const object1& a, const object1& b) + { + return a.id == b.id && *a.p == *b.p; + } + + // Multiple levels of nesting, just a compile test. + // + #pragma db object + struct object2 + { + #pragma db id(c.x) + comp2 id; + + int z; + }; +} + +// Custom/by-value access. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db value + struct comp + { + int x; + + std::string get_y () const {return y;} + void set_y (std::string v) {y = v;} + + #pragma db get(get_y) set(set_y) + std::string y; + }; + + #pragma db object + struct object + { + comp get_id () const {return id;} + void set_id (comp v) {id = v;} + + #pragma db id(y) get(get_id) set(set_id) + comp id; + + int z; + + object () {} + object (int x, std::string y, int z_): z (z_) {id.x = x; id.y = y;} + }; + + inline bool + operator== (object a, object b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z; + } +} + +// Polymorphic. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db value + struct comp + { + int x; + std::string y; + }; + + #pragma db object polymorphic + struct base + { + #pragma db id(y) + comp id; + + virtual ~base () {} + base () {} + base (int x, std::string y) {id.x = x; id.y = y;} + }; + + inline bool + operator== (const base& a, const base& b) + { + return a.id.x == b.id.x && a.id.y == b.id.y; + } + + #pragma db object + struct object: base + { + int z; + + object () {} + object (int x, std::string y, int z_): base (x, y), z (z_) {} + }; + + inline bool + operator== (const object& a, const object& b) + { + return a.id.x == b.id.x && a.id.y == b.id.y && a.z == b.z; + } +} + +#endif // TEST_HXX diff --git a/odb-tests/common/id/nested/testscript b/odb-tests/common/id/nested/testscript new file mode 100644 index 0000000..cc8f251 --- /dev/null +++ b/odb-tests/common/id/nested/testscript @@ -0,0 +1,53 @@ +# file : common/nested/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/include/.gitignore b/odb-tests/common/include/.gitignore new file mode 100644 index 0000000..d52f166 --- /dev/null +++ b/odb-tests/common/include/.gitignore @@ -0,0 +1,17 @@ +# ODB-generated files. +# +obj1-odb.?xx +obj1-odb-*.?xx +obj2-odb.?xx +obj2-odb-*.?xx +obj3-odb.?xx +obj3-odb-*.?xx + +test1-odb.?xx +test1-odb-*.?xx +test2-odb.?xx +test2-odb-*.?xx +test3-odb.?xx +test3-odb-*.?xx +test4-odb.?xx +test4-odb-*.?xx diff --git a/odb-tests/common/include/buildfile b/odb-tests/common/include/buildfile new file mode 100644 index 0000000..c9ae42e --- /dev/null +++ b/odb-tests/common/include/buildfile @@ -0,0 +1,51 @@ +# file : common/include/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = obj1 obj2 obj3 test1 test2 test3 test4 + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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). Also see driver.cxx for the details on +# the -I options usage. +# +odb_options = --table-prefix include_ \ + "-I$out_base" \ + "-I$src_base/.." \ + "-I$src_base/../.." + +cxx.poptions =+ "-I$out_base" "-I$src_base" \ + "-I$out_base/../.." "-I$src_base/../.." + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc diff --git a/odb-tests/common/include/driver.cxx b/odb-tests/common/include/driver.cxx new file mode 100644 index 0000000..561746a --- /dev/null +++ b/odb-tests/common/include/driver.cxx @@ -0,0 +1,42 @@ +// file : common/include/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test inclusion of -odb files (compilation test). +// +// The setup of this test is as follows: the ODB compiler has two +// additional include directories in its search path: .. and ../.. +// while the C++ compiler has only ../.. . This way, if a ..-based +// path is used in the generated code, the C++ compilation will +// fail. +// + +#include <memory> +#include <iostream> + +#include <odb/exceptions.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test1.hxx" +#include "test1-odb.hxx" + +#include "test2.hxx" +#include "test2-odb.hxx" + +#include "test3.hxx" +#include "test3-odb.hxx" + +#include "test4.hxx" +#include "test4-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main () +{ +} diff --git a/odb-tests/common/include/obj1.hxx b/odb-tests/common/include/obj1.hxx new file mode 100644 index 0000000..33ae0d6 --- /dev/null +++ b/odb-tests/common/include/obj1.hxx @@ -0,0 +1,25 @@ +// file : common/include/obj1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJ1_HXX +#define OBJ1_HXX + +#include <odb/core.hxx> + +#pragma db object +struct object1 +{ + object1 (unsigned long id) + : id_ (id) + { + } + + object1 () + { + } + + #pragma db id + unsigned long id_; +}; + +#endif // OBJ1_HXX diff --git a/odb-tests/common/include/obj2.hxx b/odb-tests/common/include/obj2.hxx new file mode 100644 index 0000000..2f20f58 --- /dev/null +++ b/odb-tests/common/include/obj2.hxx @@ -0,0 +1,25 @@ +// file : common/include/obj2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJ2_HXX +#define OBJ2_HXX + +#include <odb/core.hxx> + +#pragma db object +struct object2 +{ + object2 (unsigned long id) + : id_ (id) + { + } + + object2 () + { + } + + #pragma db id + unsigned long id_; +}; + +#endif // OBJ2_HXX diff --git a/odb-tests/common/include/obj3.hxx b/odb-tests/common/include/obj3.hxx new file mode 100644 index 0000000..432145b --- /dev/null +++ b/odb-tests/common/include/obj3.hxx @@ -0,0 +1,25 @@ +// file : common/include/obj3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJ3_HXX +#define OBJ3_HXX + +#include <odb/core.hxx> + +#pragma db object +struct object3 +{ + object3 (unsigned long id) + : id_ (id) + { + } + + object3 () + { + } + + #pragma db id + unsigned long id_; +}; + +#endif // OBJ3_HXX diff --git a/odb-tests/common/include/objs1.hxx b/odb-tests/common/include/objs1.hxx new file mode 100644 index 0000000..6e949e2 --- /dev/null +++ b/odb-tests/common/include/objs1.hxx @@ -0,0 +1,13 @@ +// file : common/include/objs1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJS1_HXX +#define OBJS1_HXX + +#ifdef ODB_COMPILER +# include <include/obj1.hxx> +# include <include/obj2.hxx> +# include <include/obj3.hxx> +#endif + +#endif // OBJS1_HXX diff --git a/odb-tests/common/include/objs2.hxx b/odb-tests/common/include/objs2.hxx new file mode 100644 index 0000000..4f8133b --- /dev/null +++ b/odb-tests/common/include/objs2.hxx @@ -0,0 +1,13 @@ +// file : common/include/objs2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJS2_HXX +#define OBJS2_HXX + +#ifdef ODB_COMPILER +# include "include/obj1.hxx" +# include "include/obj2.hxx" +# include "include/obj3.hxx" +#endif + +#endif // OBJS2_HXX diff --git a/odb-tests/common/include/objs3.hxx b/odb-tests/common/include/objs3.hxx new file mode 100644 index 0000000..2f7aaff --- /dev/null +++ b/odb-tests/common/include/objs3.hxx @@ -0,0 +1,11 @@ +// file : common/include/objs3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJS3_HXX +#define OBJS3_HXX + +#include "../include/obj1.hxx" +#include "../include/obj2.hxx" +#include "../include/obj3.hxx" + +#endif // OBJS3_HXX diff --git a/odb-tests/common/include/objs4.hxx b/odb-tests/common/include/objs4.hxx new file mode 100644 index 0000000..d766fe6 --- /dev/null +++ b/odb-tests/common/include/objs4.hxx @@ -0,0 +1,11 @@ +// file : common/include/objs1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef OBJS4_HXX +#define OBJS4_HXX + +#include <common/include/obj1.hxx> +#include <common/include/obj2.hxx> +#include <common/include/obj3.hxx> + +#endif // OBJS4_HXX diff --git a/odb-tests/common/include/test1.hxx b/odb-tests/common/include/test1.hxx new file mode 100644 index 0000000..1914ddb --- /dev/null +++ b/odb-tests/common/include/test1.hxx @@ -0,0 +1,16 @@ +// file : common/include/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +// Test include directive parsing. +// +#include"obj1.hxx" + + # include \ + <common/include/obj2.hxx> + +/*comment*/ # /*comment*/ include /* comment */ "obj3.hxx" // comment + +#endif // TEST1_HXX diff --git a/odb-tests/common/include/test2.hxx b/odb-tests/common/include/test2.hxx new file mode 100644 index 0000000..6017ac4 --- /dev/null +++ b/odb-tests/common/include/test2.hxx @@ -0,0 +1,15 @@ +// file : common/include/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +// Test preference of includes from the main file. +// +#include "objs1.hxx" + +#include "obj1.hxx" +#include "obj2.hxx" +#include "obj3.hxx" + +#endif // TEST2_HXX diff --git a/odb-tests/common/include/test3.hxx b/odb-tests/common/include/test3.hxx new file mode 100644 index 0000000..e55ecdb --- /dev/null +++ b/odb-tests/common/include/test3.hxx @@ -0,0 +1,12 @@ +// file : common/include/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +// Test preference of longer (more qualified) paths. +// +#include "objs2.hxx" +#include "objs3.hxx" + +#endif // TEST3_HXX diff --git a/odb-tests/common/include/test4.hxx b/odb-tests/common/include/test4.hxx new file mode 100644 index 0000000..b58e679 --- /dev/null +++ b/odb-tests/common/include/test4.hxx @@ -0,0 +1,12 @@ +// file : common/include/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +// Test preference of <> over "". +// +#include "objs2.hxx" +#include "objs4.hxx" + +#endif // TEST3_HXX diff --git a/odb-tests/common/include/testscript b/odb-tests/common/include/testscript new file mode 100644 index 0000000..c2697f5 --- /dev/null +++ b/odb-tests/common/include/testscript @@ -0,0 +1,49 @@ +# file : common/include/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* &!odb-test.db +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $* +} diff --git a/odb-tests/common/index/buildfile b/odb-tests/common/index/buildfile new file mode 100644 index 0000000..535bd26 --- /dev/null +++ b/odb-tests/common/index/buildfile @@ -0,0 +1,40 @@ +# file : common/index/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 index_ \ + --generate-schema + +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/index/driver.cxx b/odb-tests/common/index/driver.cxx new file mode 100644 index 0000000..7a22a7c --- /dev/null +++ b/odb-tests/common/index/driver.cxx @@ -0,0 +1,44 @@ +// file : common/index/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test index creation with db pragma index. See also database-specific +// tests. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + // This is just a schema creation test. + // + unique_ptr<database> db (create_database (argc, argv)); + + { + transaction t (db->begin ()); + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/index/test.hxx b/odb-tests/common/index/test.hxx new file mode 100644 index 0000000..f27783f --- /dev/null +++ b/odb-tests/common/index/test.hxx @@ -0,0 +1,142 @@ +// file : common/index/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> + +#include <odb/core.hxx> + +// Test basic functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object + struct object + { + #pragma db id auto + unsigned long id_; + + #pragma db index + int i1; + + #pragma db unique + int i2; + + #pragma db unique index + int i3; + + int i4; + #pragma db index unique member(i4) + + int i5; + #pragma db index type("UNIQUE") member(i5) + + int i6; + #pragma db index("object_i6_index") member(i6) + + int i7; + int i8; + int i9; + + int i10; + #pragma db index member(i10, "ASC") + }; + + #pragma db index(object) member(i7) + #pragma db index(object::"object_i8_index") member(i8) +} + +#pragma db index(test1::object::"object_i9_index") member(i9) + +// Test composite indexes. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db value + struct nested + { + int x; + int y; + }; + + #pragma db value + struct comp + { + int x; + int y; + nested n; + }; + + #pragma db object + struct object + { + #pragma db id auto + unsigned long id_; + + int i1a; + int i1b; + #pragma db index("object_i1_i") member(i1a) member(i1b) + + int i2a; + int i2b; + #pragma db index("object_i2_i") members(i2a, i2b) + + #pragma db index + comp c1; + + #pragma db index column("") + comp c2; + + comp c3; + #pragma db index member(c3.x) + #pragma db index member(c3.y) + + comp c4; + #pragma db index("object_c4_i") members(c4.x, c4.y, c4.n.x) + + comp c5; + int i5; + #pragma db index("object_ci5_i") member(c5) member(i5) + }; +} + +// Test container indexes. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db value + struct id + { + int x; + int y; + }; + + #pragma db value + struct comp + { + int x; + std::vector<int> v; + }; + + #pragma db object + struct object + { + #pragma db id + id id_; + + std::vector<int> v; + #pragma db index unique member(v.id) + #pragma db index("object_v_index_index") member(v.index) + + comp c; + #pragma db index("object_c_v_id_index") member(c.v.id) + #pragma db index unique member(c.v.index) + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/index/testscript b/odb-tests/common/index/testscript new file mode 100644 index 0000000..1355726 --- /dev/null +++ b/odb-tests/common/index/testscript @@ -0,0 +1,53 @@ +# file : common/index/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/inheritance/polymorphism/.gitignore b/odb-tests/common/inheritance/polymorphism/.gitignore new file mode 100644 index 0000000..f183a6f --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/.gitignore @@ -0,0 +1,76 @@ +# ODB-generated files. +# +test1-odb.?xx +test1-odb-*.?xx +test1.sql +test1-*.sql + +test2-odb.?xx +test2-odb-*.?xx +test2.sql +test2-*.sql + +test3-odb.?xx +test3-odb-*.?xx +test3.sql +test3-*.sql + +test4-odb.?xx +test4-odb-*.?xx +test4.sql +test4-*.sql + +test5-odb.?xx +test5-odb-*.?xx +test5.sql +test5-*.sql + +test6-odb.?xx +test6-odb-*.?xx +test6.sql +test6-*.sql + +test7-odb.?xx +test7-odb-*.?xx +test7.sql +test7-*.sql + +test8-odb.?xx +test8-odb-*.?xx +test8.sql +test8-*.sql + +test9-odb.?xx +test9-odb-*.?xx +test9.sql +test9-*.sql + +test10-odb.?xx +test10-odb-*.?xx +test10.sql +test10-*.sql + +test11-odb.?xx +test11-odb-*.?xx +test11.sql +test11-*.sql + +test12-odb.?xx +test12-odb-*.?xx +test12.sql +test12-*.sql + +test13-odb.?xx +test13-odb-*.?xx +test13.sql +test13-*.sql + +test14-odb.?xx +test14-odb-*.?xx +test14.sql +test14-*.sql + +test15-odb.?xx +test15-odb-*.?xx +test15.sql +test15-*.sql diff --git a/odb-tests/common/inheritance/polymorphism/buildfile b/odb-tests/common/inheritance/polymorphism/buildfile new file mode 100644 index 0000000..40fc978 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/buildfile @@ -0,0 +1,52 @@ +# file : common/inheritance/polymorphism/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \ + test12 test13 test14 test15 + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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 inhrt_p_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../../alias{database-client}: include = adhoc + +testscript@./: schemas = $hdrs diff --git a/odb-tests/common/inheritance/polymorphism/driver.cxx b/odb-tests/common/inheritance/polymorphism/driver.cxx new file mode 100644 index 0000000..12f4666 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/driver.cxx @@ -0,0 +1,2093 @@ +// file : common/inheritance/polymorphism/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test polymorphic object inheritance. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test1.hxx" +#include "test2.hxx" +#include "test3.hxx" +#include "test4.hxx" +#include "test5.hxx" +#include "test6.hxx" +#include "test7.hxx" +#include "test8.hxx" +#include "test9.hxx" +#include "test10.hxx" +#include "test11.hxx" +#include "test12.hxx" +#include "test13.hxx" +#include "test14.hxx" +#include "test15.hxx" + +#include "test1-odb.hxx" +#include "test2-odb.hxx" +#include "test3-odb.hxx" +#include "test4-odb.hxx" +#include "test5-odb.hxx" +#include "test6-odb.hxx" +#include "test7-odb.hxx" +#include "test8-odb.hxx" +#include "test9-odb.hxx" +#include "test10-odb.hxx" +#include "test11-odb.hxx" +#include "test12-odb.hxx" +#include "test13-odb.hxx" +#include "test14-odb.hxx" +#include "test15-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +const char* events[] = +{ + "pre_persist", + "post_persist", + "pre_load", + "post_load", + "pre_update", + "post_update", + "pre_erase", + "post_erase" +}; + +namespace test6 +{ + void base:: + db_callback (callback_event e, database&) + { + cout << "base " << events[e] << " " << id << endl; + } + + void base:: + db_callback (callback_event e, database&) const + { + cout << "base " << events[e] << " " << id << " const" << endl; + } + + void derived:: + db_callback (callback_event e, database&) const + { + cout << "derived " << events[e] << " " << id << " const" << endl; + } +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test 1: basic polymorphism functionality. + // + { + using namespace test1; + + root r (1, 1); + base b (2, 2, "bbb"); + derived d (3, 3, "ddd"); + + r.strs.push_back ("a"); + r.strs.push_back ("aa"); + r.strs.push_back ("aaa"); + + b.nums.push_back (21); + b.nums.push_back (22); + b.nums.push_back (23); + b.strs.push_back ("b"); + b.strs.push_back ("bb"); + b.strs.push_back ("bbb"); + + d.nums.push_back (31); + d.nums.push_back (32); + d.nums.push_back (33); + d.strs.push_back ("d"); + d.strs.push_back ("dd"); + d.strs.push_back ("ddd"); + + { + transaction t (db->begin ()); + + // Static persist. + // + db->persist (r); + db->persist (b); + + // Dynamic persist. + // + root& r (d); + db->persist (r); + + t.commit (); + } + + // Static load. + // + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + t.commit (); + + assert (*pr == r); + assert (*pb == b); + assert (*pd == d); + } + + // Dynamic load. + // + { + transaction t (db->begin ()); + unique_ptr<root> pb (db->load<root> (b.id)); + unique_ptr<root> pd1 (db->load<root> (d.id)); + unique_ptr<base> pd2 (db->load<base> (d.id)); + t.commit (); + + assert (*pb == b); + assert (*pd1 == d); + assert (*pd2 == d); + } + + // Invalid load. + // + { + transaction t (db->begin ()); + + try + { + unique_ptr<base> p (db->load<base> (r.id)); + assert (false); + } + catch (const object_not_persistent&) {} + + try + { + unique_ptr<derived> p (db->load<derived> (b.id)); + assert (false); + } + catch (const object_not_persistent&) {} + + t.commit (); + } + + // Static load into existing instance. + // + { + transaction t (db->begin ()); + root r1; + db->load (r.id, r1); + base b1; + db->load (b.id, b1); + derived d1; + db->load (d.id, d1); + t.commit (); + + assert (r1 == r); + assert (b1 == b); + assert (d1 == d); + } + + // Dynamic load into existing instance. + // + { + transaction t (db->begin ()); + base b1; + db->load (b.id, static_cast<root&> (b1)); + derived d1; + db->load (d.id, static_cast<base&> (d1)); + t.commit (); + + assert (b1 == b); + assert (d1 == d); + } + + // Invalid load into existing instance. + // + { + transaction t (db->begin ()); + + try + { + base b; + db->load (r.id, static_cast<root&> (b)); + assert (false); + } + catch (const object_not_persistent&) {} + + try + { + derived d; + db->load (b.id, static_cast<base&> (d)); + assert (false); + } + catch (const object_not_persistent&) {} + + t.commit (); + } + + // Slicing load. + // + { + transaction t (db->begin ()); + root b1; + db->load (b.id, b1); + base d1; + db->load (d.id, d1); + t.commit (); + + assert (b1 == static_cast<root> (b)); + assert (d1 == static_cast<base> (d)); + } + + // Static reload. + // + { + transaction t (db->begin ()); + root r1; + r1.id = r.id; + db->reload (r1); + base b1; + b1.id = b.id; + db->reload (b1); + derived d1; + d1.id = d.id; + db->reload (d1); + t.commit (); + + assert (r1 == r); + assert (b1 == b); + assert (d1 == d); + } + + // Dynamic reload. + // + { + transaction t (db->begin ()); + base b1; + b1.id = b.id; + db->reload (static_cast<root&> (b1)); + derived d1; + d1.id = d.id; + db->reload (static_cast<base&> (d1)); + t.commit (); + + assert (b1 == b); + assert (d1 == d); + } + + // Invalid reload. + // + { + transaction t (db->begin ()); + + try + { + base b; + b.id = r.id; + db->reload (static_cast<root&> (b)); + assert (false); + } + catch (const object_not_persistent&) {} + + try + { + derived d; + d.id = b.id; + db->reload (static_cast<base&> (d)); + assert (false); + } + catch (const object_not_persistent&) {} + + t.commit (); + } + + // Slicing reload. + // + { + transaction t (db->begin ()); + root b1; + b1.id = b.id; + db->reload (b1); + base d1; + d1.id = d.id; + db->reload (d1); + t.commit (); + + assert (b1 == static_cast<root> (b)); + assert (d1 == static_cast<base> (d)); + } + + // Query. + // + { + typedef odb::query<root> root_query; + typedef odb::result<root> root_result; + + typedef odb::query<base> base_query; + typedef odb::result<base> base_result; + + typedef odb::result<derived> derived_result; + + transaction t (db->begin ()); + + // Test loading via root. + // + { + root_result qr (db->query<root> ("ORDER BY" + root_query::id)); + root_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == r); + assert (++i != e && *i == b); + assert (++i != e && *i == d); + assert (++i == e); + } + + // Test loading via base. + // + { + base_result qr (db->query<base> ("ORDER BY" + base_query::id)); + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == b); + assert (++i != e && *i == d); + assert (++i == e); + } + + // Test loading via derived. + // + { + derived_result qr (db->query<derived> ()); + derived_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == d); + assert (++i == e); + } + + // Test loading into an existing instance. + // + { + root_result qr (db->query<root> ()); + + unsigned short mask (0); + + for (root_result::iterator i (qr.begin ()); i != qr.end (); ++i) + { + string ds (i.discriminator ()); + + if (ds == "test1::root") + { + root r1; + i.load (r1); + assert (r1 == r); + mask |= 1; + } + else if (ds == "test1::base") + { + base b1; + i.load (b1); + assert (b1 == b); + mask |= 2; + } + else if (ds == "test1::derived") + { + derived d1; + i.load (d1); + assert (d1 == d); + mask |= 4; + } + else + assert (false); + } + + assert (mask == 7); + } + + // Test query conditions with columns from multiple tables. + // + { + base_result qr ( + db->query<base> ( + base_query::num == 3 && base_query::str == "ddd")); + + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == d); + assert (++i == e); + } + + // Test discriminator access. + // + { + base_result qr (db->query<base> (base_query::id == 3)); + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i.discriminator () == "test1::derived"); + assert (++i == e); + } + + // Test loading of an object from the same hierarchy during + // query result iteration (tests image copying via change + // callbacks in some databases). + // + { + base_result qr (db->query<base> ()); + + unsigned short mask (0); + + for (base_result::iterator i (qr.begin ()); i != qr.end (); ++i) + { + string ds (i.discriminator ()); + + if (ds == "test1::base") + { + unique_ptr<derived> d1 (db->load<derived> (d.id)); + assert (*d1 == d); + assert (*i == b); + mask |= 1; + } + else if (ds == "test1::derived") + { + unique_ptr<base> b1 (db->load<base> (b.id)); + assert (*b1 == b); + assert (*i == d); + mask |= 2; + } + } + + assert (mask == 3); + } + + t.commit (); + } + + // Views. + // + { + typedef odb::query<root_view> root_query; + typedef odb::result<root_view> root_result; + + typedef odb::query<base_view> base_query; + typedef odb::result<base_view> base_result; + + typedef odb::result<derived_view> derived_result; + + transaction t (db->begin ()); + + // root + // + { + root_result qr (db->query<root_view> ("ORDER BY" + root_query::id)); + root_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i->typeid_ == "test1::root" && i->num == r.num); + assert (++i != e && i->typeid_ == "test1::base" && i->num == b.num); + assert (++i != e && i->typeid_ == "test1::derived" && i->num == d.num); + assert (++i == e); + } + + // base + // + { + base_result qr (db->query<base_view> ("ORDER BY" + base_query::id)); + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->id == b.id && i->num == b.num && i->str == b.str); + assert (++i != e && + i->id == d.id && i->num == d.num && i->str == d.str); + assert (++i == e); + } + + // derived + // + { + derived_result qr (db->query<derived_view> ()); + derived_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->num == d.num && i->str == d.str && + i->dnum == d.dnum && i->dstr == d.dstr); + assert (++i == e); + } + + t.commit (); + } + + // Update. + // + r.num++; + r.strs.push_back ("aaaa"); + + b.num++; + b.str += "b"; + b.nums.push_back (24); + b.strs.push_back ("bbbb"); + + d.num++; + d.str += "d"; + d.dnum++; + d.dstr += "d"; + d.nums.push_back (34); + d.strs.push_back ("dddd"); + + { + transaction t (db->begin ()); + + // Static update. + // + db->update (r); + db->update (b); + + // Dynamic update. + // + root& r (d); + db->update (r); + + t.commit (); + } + + // Verify update. + // + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + t.commit (); + + assert (*pr == r); + assert (*pb == b); + assert (*pd == d); + } + + // Invalid erase via id. + // + { + transaction t (db->begin ()); + + try + { + db->erase<base> (r.id); + assert (false); + } + catch (const object_not_persistent&) {} + + try + { + db->erase<derived> (b.id); + assert (false); + } + catch (const object_not_persistent&) {} + + t.commit (); + } + + // Static erase via id. + // + { + transaction t (db->begin ()); + db->erase<root> (r.id); + db->erase<base> (b.id); + t.commit (); + } + + // Dynamic erase via id. + // + { + transaction t (db->begin ()); + db->erase<root> (d.id); + t.commit (); + } + + { + transaction t (db->begin ()); + db->persist (r); + db->persist (b); + db->persist (d); + t.commit (); + } + + // Static erase via object. + // + { + transaction t (db->begin ()); + db->erase (r); + db->erase (b); + t.commit (); + } + + // Dynamic erase via object. + // + { + const root& r (d); + transaction t (db->begin ()); + db->erase (r); + t.commit (); + } + } + + // Test 2: inverse object pointers in polymorhic bases. + // + { + using namespace test2; + + derived d (1, "d", 1); + root_pointer rp (&d); + base_pointer bp (&d); + + { + transaction t (db->begin ()); + db->persist (rp); + db->persist (bp); + + d.rp.reset (*db, &rp); + d.bp.reset (*db, &bp); + + db->persist (d); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + unique_ptr<base> pb (db->load<base> (d.id)); + unique_ptr<root> pr (db->load<root> (d.id)); + t.commit (); + + assert (pd->rp.object_id<root_pointer> () == rp.id && + pd->bp.object_id<base_pointer> () == bp.id); + + derived* p (dynamic_cast<derived*> (pb.get ())); + assert (p != 0 && + p->rp.object_id<root_pointer> () == rp.id && + p->bp.object_id<base_pointer> () == bp.id); + + p = dynamic_cast<derived*> (pr.get ()); + assert (p != 0 && + p->rp.object_id<root_pointer> () == rp.id && + p->bp.object_id<base_pointer> () == bp.id); + } + + // Query. + // + { + typedef odb::query<base> base_query; + typedef odb::result<base> base_result; + + transaction t (db->begin ()); + + // Test query conditions with columns in pointed-to objects from + // multiple tables. + // + { + base_result qr ( + db->query<base> ( + base_query::rp->id == rp.id && + base_query::bp->id == bp.id)); + + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i.discriminator () == "test2::derived"); + assert (++i == e); + } + + t.commit (); + } + + // Views. + // + { + typedef odb::result<root_view> root_result; + typedef odb::result<base_view> base_result; + + transaction t (db->begin ()); + + // root + // + { + root_result qr (db->query<root_view> ()); + root_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i->rp_id == rp.id && i->r_id == d.id); + assert (++i == e); + } + + // base + // + { + base_result qr (db->query<base_view> ()); + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->bp_id == bp.id && i->b_id == d.id && i->str == d.str); + assert (++i == e); + } + + t.commit (); + } + } + + // Test 3: delayed loading. + // + { + using namespace test3; + + base b1 (21, 21); + base b2 (22, 22); + base b3 (23, 23); + + derived d1 (31, 31, "d"); + derived d2 (32, 32, "dd"); + derived d3 (33, 33, "ddd"); + + b1.rptr = new root (1); + b2.rptr = new base (2, 2); + b3.rptr = new derived (3, 3, "b3"); + + d1.rptr = new root (4); + d2.rptr = new base (5, 5); + d3.rptr = new derived (6, 6, "d3"); + + d2.bptr = new base (7, 7); + d3.bptr = new derived (8, 8, "d3b"); + + { + transaction t (db->begin ()); + db->persist (b1); + db->persist (b2); + db->persist (b3); + + db->persist (d1); + db->persist (d2); + db->persist (d3); + + db->persist (b1.rptr); + db->persist (b2.rptr); + db->persist (b3.rptr); + + db->persist (d1.rptr); + db->persist (d2.rptr); + db->persist (d3.rptr); + + db->persist (d2.bptr); + db->persist (d3.bptr); + + t.commit (); + } + + { + transaction t (db->begin ()); + + { + unique_ptr<base> p1 (db->load<base> (b1.id)); + unique_ptr<base> p2 (db->load<base> (b2.id)); + unique_ptr<root> p3 (db->load<root> (b3.id)); + assert (*p1 == b1); + assert (*p2 == b2); + assert (*p3 == b3); + } + + { + unique_ptr<derived> p1 (db->load<derived> (d1.id)); + unique_ptr<base> p2 (db->load<base> (d2.id)); + unique_ptr<root> p3 (db->load<root> (d3.id)); + assert (*p1 == d1); + assert (*p2 == d2); + assert (*p3 == d3); + } + + t.commit (); + } + + // Query. + // + { + typedef odb::query<derived> derived_query; + typedef odb::result<derived> derived_result; + + transaction t (db->begin ()); + + // Test query conditions with columns in pointed-to objects from + // multiple tables. + // + { + derived_result qr ( + db->query<derived> ( + derived_query::rptr->id == 6 && + derived_query::bptr->id == 8 && + derived_query::bptr->num == 8)); + + derived_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == d3); + assert (++i == e); + } + + t.commit (); + } + + // Views. + // + { + typedef odb::query<base_view> base_query; + typedef odb::result<base_view> base_result; + + typedef odb::query<derived_view> derived_query; + typedef odb::result<derived_view> derived_result; + + typedef odb::query<root_view> root_query; + typedef odb::result<root_view> root_result; + + transaction t (db->begin ()); + + // base + // + { + base_result qr ( + db->query<base_view> ( + base_query::base::num == b2.num && + base_query::base::id == b2.id && + base_query::r::id == b2.rptr->id)); + + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->b_id == b2.id && + i->r_id == b2.rptr->id && + i->num == b2.num); + assert (++i == e); + } + + // derived + // + { + derived_result qr ( + db->query<derived_view> ( + derived_query::d::str == d3.str && + derived_query::d::num == d3.num && + derived_query::b::num == d3.bptr->num && + derived_query::d::id == d3.id && + derived_query::b::id == d3.bptr->id && + derived_query::r::id == d3.rptr->id)); + + derived_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->d_id == d3.id && + i->b_id == d3.bptr->id && + i->r_id == d3.rptr->id && + i->d_num == d3.num && + i->b_num == d3.bptr->num && + i->str == d3.str); + assert (++i == e); + } + + // root + // + { + root_result qr ( + db->query<root_view> ( + root_query::r::id.in (b2.rptr->id, d2.rptr->id))); + + root_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && + i->r_id == d2.rptr->id && + i->d_id == d2.id && + i->str == d2.str); + assert (++i == e); + } + + t.commit (); + } + } + + // Test 4: views. + // + { + using namespace test4; + + base1 b1 (21, 1); + + root2 r2 (11, 0); + base2 b2 (21, 1, "abc"); + + { + transaction t (db->begin ()); + db->persist (b1); + db->persist (r2); + db->persist (b2); + t.commit (); + } + + { + typedef odb::query<view1> query; + typedef odb::result<view1> result; + + transaction t (db->begin ()); + + { + result qr ( + db->query<view1> ( + query::base1::num == b1.num)); + + result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i->str == "abc"); + assert (++i == e); + } + + t.commit (); + } + + { + typedef odb::result<view2> result; + + transaction t (db->begin ()); + + { + result qr (db->query<view2> ()); + result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i->min_num == 1); + assert (++i == e); + } + + t.commit (); + } + + { + typedef odb::result<view3> result; + + transaction t (db->begin ()); + + { + result qr (db->query<view3> ()); + result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && i->str == "abc"); + assert (++i == e); + } + + t.commit (); + } + } + + // Test 5: polymorphism and optimistic concurrency. + // + { + using namespace test5; + + root r (1, 1); + base b (2, 2, "bbb"); + derived d (3, 3, "ddd"); + + r.strs.push_back ("a"); + r.strs.push_back ("aa"); + r.strs.push_back ("aaa"); + + b.nums.push_back (21); + b.nums.push_back (22); + b.nums.push_back (23); + b.strs.push_back ("b"); + b.strs.push_back ("bb"); + b.strs.push_back ("bbb"); + + d.nums.push_back (31); + d.nums.push_back (32); + d.nums.push_back (33); + d.strs.push_back ("d"); + d.strs.push_back ("dd"); + d.strs.push_back ("ddd"); + + { + transaction t (db->begin ()); + db->persist (r); + db->persist (b); + db->persist (d); + t.commit (); + } + + // Update. + // + { + transaction t (db->begin ()); + + // Root. + // + { + unique_ptr<root> p (db->load<root> (r.id)); + + r.num++; + r.strs.push_back ("aaaa"); + db->update (r); + + p->num--; + p->strs.pop_back (); + try + { + db->update (p); + assert (false); + } + catch (const odb::object_changed&) {} + + // Make sure the object is intact. + // + db->reload (p); + assert (r == *p); + } + + // Base. + // + { + unique_ptr<base> p (db->load<base> (b.id)); + + b.num++; + b.str += "b"; + b.strs.push_back ("bbbb"); + b.nums.push_back (24); + db->update (b); + + p->num--; + p->str += "B"; + p->strs.pop_back (); + p->nums.pop_back (); + try + { + db->update (p); + assert (false); + } + catch (const odb::object_changed&) {} + + // Make sure the object is intact. + // + db->reload (p); + assert (b == *p); + } + + // Derived. + // + { + unique_ptr<root> p (db->load<root> (d.id)); // Via root. + + d.num++; + d.str += "d"; + d.strs.push_back ("dddd"); + d.nums.push_back (24); + d.dnum++; + d.dstr += "d"; + db->update (d); + + derived& d1 (static_cast<derived&> (*p)); + d1.num--; + d1.str += "D"; + d1.strs.pop_back (); + d1.nums.pop_back (); + d1.dnum--; + d1.dstr += "D"; + try + { + db->update (p); + assert (false); + } + catch (const odb::object_changed&) {} + + // Make sure the object is intact. + // + db->reload (p); + assert (d == *p); + } + + t.commit (); + } + + // Reload. + // + { + transaction t (db->begin ()); + + // Make sure reload doesn't modify the object if the versions + // match. + // + derived d1 (d); + d1.num++; + d1.str += "d"; + d1.strs.push_back ("dddd"); + d1.nums.push_back (24); + d1.dnum++; + d1.dstr += "d"; + derived d2 (d1); + + db->reload (d1); + assert (d1 == d2); + + t.commit (); + } + + // Erase. + // + { + transaction t (db->begin ()); + + // Root. + // + { + unique_ptr<root> p (db->load<root> (r.id)); + + r.num++; + r.strs.push_back ("aaaaa"); + db->update (r); + + try + { + db->erase (p); + assert (false); + } + catch (const odb::object_changed&) {} + + db->reload (p); + db->erase (p); + } + + // Base. + // + { + unique_ptr<base> p (db->load<base> (b.id)); + + b.num++; + b.str += "b"; + b.strs.push_back ("bbbb"); + b.nums.push_back (24); + db->update (b); + + try + { + db->erase (p); + assert (false); + } + catch (const odb::object_changed&) {} + + db->reload (p); + db->erase (p); + } + + // Derived. + // + { + unique_ptr<root> p (db->load<root> (d.id)); // Via root. + + d.num++; + d.str += "d"; + d.strs.push_back ("dddd"); + d.nums.push_back (24); + d.dnum++; + d.dstr += "d"; + db->update (d); + + try + { + db->erase (p); + assert (false); + } + catch (const odb::object_changed&) {} + + db->reload (p); + db->erase (p); + } + + // Try to update non-existent object. + // + { + try + { + db->update (d); + assert (false); + } + catch (const odb::object_changed&) {} + } + + // Try to erase non-existent object. + // + { + try + { + db->erase (d); + assert (false); + } + catch (const odb::object_changed&) {} + } + + t.commit (); + } + } + + // Test 6: polymorphism and callbacks. + // + { + using namespace test6; + + base b (1, 1, "bbb"); + + unique_ptr<base> d (new derived (2, 2, "ddd")); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<root> pd (db->load<root> (d->id)); + + db->load (b.id, *pb); + db->load (d->id, *pd); + + db->reload (*pb); + db->reload (*pd); + + t.commit (); + } + + // Update. + // + { + b.num++; + d->num++; + + transaction t (db->begin ()); + db->update (b); + db->update (d); + t.commit (); + } + + // Query. + // + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + + result r (db->query<base> ("ORDER BY" + query::id)); + for (result::iterator i (r.begin ()); i != r.end (); ++i) + *i; + + t.commit (); + } + + // Erase. + // + { + transaction t (db->begin ()); + db->erase (b); + db->erase (d); + t.commit (); + } + + // Recursive (delayed) loading. + // + { + derived d (3, 3, "dddd"); + d.ptr.reset (new derived (4, 4, "ddddd")); + + { + transaction t (db->begin ()); + db->persist (d); + db->persist (d.ptr); + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<root> p (db->load<root> (d.id)); + t.commit (); + } + } + } + + // Test 7: polymorphism and object cache (session). + // + { + using namespace test7; + + shared_ptr<root> r (new root (1, 1)); + shared_ptr<base> b (new base (2, 2, "b")); + shared_ptr<root> d (new derived (3, 3, "d")); + + // Persist. + // + { + session s; + + { + transaction t (db->begin ()); + db->persist (r); + db->persist (b); + db->persist (d); + t.commit (); + } + + assert (db->load<root> (r->id) == r); + assert (db->load<base> (b->id) == b); + assert (db->load<root> (d->id) == d); + } + + // Load. + // + { + session s; + + transaction t (db->begin ()); + shared_ptr<root> r1 (db->load<root> (r->id)); + shared_ptr<base> b1 (db->load<base> (b->id)); + shared_ptr<derived> d1 (db->load<derived> (d->id)); + t.commit (); + + assert (db->load<root> (r->id) == r1); + assert (db->load<base> (b->id) == b1); + assert (db->load<root> (d->id) == d1); + + assert (!db->find<derived> (b->id)); + } + + // Query. + // + { + typedef odb::query<root> query; + typedef odb::result<root> result; + + session s; + + transaction t (db->begin ()); + shared_ptr<root> r1 (db->load<root> (r->id)); + shared_ptr<base> b1 (db->load<base> (b->id)); + shared_ptr<derived> d1 (db->load<derived> (d->id)); + t.commit (); + + { + transaction t (db->begin ()); + + result r (db->query<root> ("ORDER BY" + query::id)); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e && i.load () == r1); + assert (++i != e && i.load () == b1); + assert (++i != e && i.load () == d1); + assert (++i == e); + + t.commit (); + } + } + + // Erase. + // + { + session s; + + { + transaction t (db->begin ()); + db->load<root> (r->id); + db->load<root> (b->id); + db->load<root> (d->id); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (r); + db->erase (b); + db->erase (d); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (!db->find<root> (r->id)); + assert (!db->find<base> (b->id)); + assert (!db->find<root> (d->id)); + t.commit (); + } + } + } + + // Test 8: polymorphism and abstract bases. + // + { + using namespace test8; + + base b (1, 1, "b"); + interm i (2, 2, "i", true); + derived1 d1 (3, 3, "d1", true); + derived2 d2 (4, 4, "d2", false); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (static_cast<root&> (d1)); + db->persist (static_cast<interm&> (d2)); + + try + { + db->persist (i); + assert (false); + } + catch (const odb::abstract_class&) {} + + try + { + db->persist (static_cast<base&> (i)); + assert (false); + } + catch (const odb::no_type_info&) {} + + t.commit (); + } + + // Load. + // + { + base vb; + interm vi; + derived1 vd1; + derived2 vd2; + + transaction t (db->begin ()); + + // load (id) + // + unique_ptr<root> pb (db->load<root> (b.id)); + unique_ptr<interm> pd1 (db->load<interm> (d1.id)); + unique_ptr<derived2> pd2 (db->load<derived2> (d2.id)); + + assert (*pb == b); + assert (*pd1 == d1); + assert (*pd2 == d2); + + // load (id, obj) + // + db->load (b.id, static_cast<root&> (vb)); + db->load (d1.id, static_cast<base&> (vd1)); + db->load (d2.id, static_cast<interm&> (vd2)); + + assert (vb == b); + assert (vd1 == d1); + assert (vd2 == d2); + + try + { + db->load (i.id, static_cast<root&> (vi)); + assert (false); + } + catch (const odb::no_type_info&) {} + + // reload (obj) + // + vb.num = 0; + vd1.num = 0; + vd2.num = 0; + + db->reload (static_cast<root&> (vb)); + db->reload (static_cast<base&> (vd1)); + db->reload (static_cast<interm&> (vd2)); + + assert (vb == b); + assert (vd1 == d1); + assert (vd2 == d2); + + try + { + db->reload (static_cast<root&> (vi)); + assert (false); + } + catch (const odb::no_type_info&) {} + + t.commit (); + } + + // Update. + // + { + b.num++; + b.str += 'b'; + d1.num++; + d1.str += "d1"; + d2.num++; + d2.str += "d1"; + + { + transaction t (db->begin ()); + db->update (static_cast<root&> (b)); + db->update (d1); + db->update (static_cast<interm&> (d2)); + + try + { + db->update (i); + assert (false); + } + catch (const odb::abstract_class&) {} + + try + { + db->update (static_cast<base&> (i)); + assert (false); + } + catch (const odb::no_type_info&) {} + + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<root> pd1 (db->load<root> (d1.id)); + unique_ptr<base> pd2 (db->load<base> (d2.id)); + + t.commit (); + + assert (*pb == b); + assert (*pd1 == d1); + assert (*pd2 == d2); + } + } + + // Erase. + // + { + transaction t (db->begin ()); + db->erase (b); + db->erase<interm> (d1.id); + db->erase (static_cast<root&> (d2)); + + try + { + db->erase (i); + assert (false); + } + catch (const odb::abstract_class&) {} + + try + { + db->erase (static_cast<base&> (i)); + assert (false); + } + catch (const odb::no_type_info&) {} + + t.commit (); + } + } + + // Test 9: polymorphism and readonly classes. + // + { + using namespace test9; + + ro_root ro_r (1, 1); + rw_base rw_b (2, 2, "b"); + ro_derived ro_d (3, 3, "d"); + + rw_root rw_r (1, 1); + ro_base ro_b (2, 2, "b"); + rw_derived rw_d (3, 3, "d"); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (ro_r); + db->persist (rw_b); + db->persist (ro_d); + + db->persist (rw_r); + db->persist (ro_b); + db->persist (rw_d); + t.commit (); + } + + // Update. + // + { + ro_root ro_r1 (ro_r); + rw_base rw_b1 (rw_b); + ro_derived ro_d1 (ro_d); + + ro_base ro_b1 (ro_b); + rw_derived rw_d1 (rw_d); + + ro_r1.num++; + ro_r1.strs.push_back ("b"); + + rw_b1.num++; + rw_b1.strs.push_back ("b"); + rw_b1.str += "b"; + rw_b1.nums.push_back (2); + rw_b.str += "b"; + rw_b.nums.push_back (2); + + ro_d1.num++; + ro_d1.strs.push_back ("d"); + ro_d1.str += "d"; + ro_d1.nums.push_back (3); + ro_d1.dnum++; + ro_d1.dstr += "d"; + + rw_r.num++; + rw_r.strs.push_back ("b"); + + ro_b1.num++; + ro_b1.strs.push_back ("b"); + ro_b1.str += "b"; + ro_b1.nums.push_back (2); + + rw_d1.num++; + rw_d1.strs.push_back ("d"); + rw_d1.str += "d"; + rw_d1.nums.push_back (3); + rw_d1.dnum++; + rw_d1.dstr += "d"; + rw_d.dnum++; + rw_d.dstr += "d"; + + { + // These should be no-ops. + // + db->update (ro_r1); + db->update (static_cast<ro_root&> (ro_d1)); + db->update (ro_b1); + + transaction t (db->begin ()); + db->update (static_cast<ro_root&> (rw_b1)); + db->update (rw_r); + db->update (static_cast<ro_base&> (rw_d1)); + t.commit (); + } + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<ro_root> p_ro_r (db->load<ro_root> (ro_r.id)); + unique_ptr<ro_root> p_rw_b (db->load<ro_root> (rw_b.id)); + unique_ptr<ro_root> p_ro_d (db->load<ro_root> (ro_d.id)); + + unique_ptr<rw_root> p_rw_r (db->load<rw_root> (rw_r.id)); + unique_ptr<rw_root> p_ro_b (db->load<rw_root> (ro_b.id)); + unique_ptr<rw_root> p_rw_d (db->load<rw_root> (rw_d.id)); + + t.commit (); + + assert (*p_ro_r == ro_r); + assert (*p_rw_b == rw_b); + assert (*p_ro_d == ro_d); + + assert (*p_rw_r == rw_r); + assert (*p_ro_b == ro_b); + assert (*p_rw_d == rw_d); + } + } + + // Test 10: empty polymorphic classes. + // + { + using namespace test10; + + base b (1, 1); + derived d (2, 2); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (static_cast<root&> (d)); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<root> pb (db->load<root> (b.id)); + unique_ptr<root> pd (db->load<root> (d.id)); + t.commit (); + + assert (*pb == b); + assert (*pd == d); + } + + // Update. + // + { + b.num++; + d.num++; + + transaction t (db->begin ()); + db->update (static_cast<root&> (b)); + db->update (d); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<root> pb (db->load<root> (b.id)); + unique_ptr<root> pd (db->load<root> (d.id)); + t.commit (); + + assert (*pb == b); + assert (*pd == d); + } + } + + // Test 11: reuse and polymorphic inheritance. + // + { + using namespace test11; + + base b (1, 1, "b"); + derived d (2, 2, "d"); + + b.strs.push_back ("b"); + b.nums.push_back (1); + + d.strs.push_back ("d"); + d.nums.push_back (1); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (static_cast<base&> (d)); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<base> pd (db->load<base> (d.id)); + t.commit (); + + assert (*pb == b); + assert (*pd == d); + } + + // Update. + // + { + b.num++; + b.str += "b"; + b.strs.push_back ("bb"); + b.nums.push_back (2); + + d.num++; + d.str += "d"; + d.strs.push_back ("dd"); + d.nums.push_back (2); + d.dnum++; + d.dstr += "d"; + + transaction t (db->begin ()); + db->update (b); + db->update (d); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<base> pd (db->load<base> (d.id)); + + t.commit (); + + assert (*pb == b); + assert (*pd == d); + } + + // Query. + // + { + typedef odb::query<base> base_query; + typedef odb::result<base> base_result; + + typedef odb::query<derived> derived_query; + typedef odb::result<derived> derived_result; + + transaction t (db->begin ()); + + { + base_result qr (db->query<base> (base_query::num == 2)); + base_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == b); + assert (++i == e); + } + + { + derived_result qr (db->query<derived> (derived_query::num == 3)); + derived_result::iterator i (qr.begin ()), e (qr.end ()); + + assert (i != e && *i == d); + assert (++i == e); + } + + t.commit (); + } + } + + // Test 12: polymorphic objects with auto id. + // + { + using namespace test12; + + base b (1); + derived d (2); + + unsigned long id1, id2; + + // Persist. + // + { + transaction t (db->begin ()); + id1 = db->persist (b); + id2 = db->persist (static_cast<root&> (d)); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<root> pb (db->load<root> (id1)); + unique_ptr<root> pd (db->load<root> (id2)); + + t.commit (); + + assert (*pb == b); + assert (*pd == d); + } + } + + // Test 13: polymorphic derived without any non-container data members + // (which results in an empty SELECT statement). + // + { + using namespace test13; + + base b; + b.nums.push_back (123); + derived d; + d.nums.push_back (123); + d.strs.push_back ("abc"); + + base1 b1; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + db->persist (b1); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<root> pbr (db->load<root> (b.id)); + unique_ptr<root> pdr (db->load<root> (d.id)); + unique_ptr<base> pdb (db->load<base> (d.id)); + unique_ptr<root> pb1r (db->load<root> (b1.id)); + t.commit (); + + base& rb (static_cast<base&> (*pbr)); + derived& rd1 (static_cast<derived&> (*pdr)); + derived& rd2 (static_cast<derived&> (*pdb)); + base1 rb1 (static_cast<base1&> (*pb1r)); + + assert (rb.id == b.id && rb.nums == b.nums); + assert (rd1.id == d.id && rd1.nums == rd1.nums && + rd1.strs == rd1.strs); + assert (rd2.id == d.id && rd2.nums == rd2.nums && + rd2.strs == rd2.strs); + assert (rb1.id == b1.id); + } + } + + // Test 14: inverse pointer in polymorphic base. + // + { + using namespace test14; + + derived d; + d.num = 123; + + d.o1 = new object1; + d.o2 = new object2; + d.o3.push_back (new object3); + d.o4.push_back (new object4); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (d.o1); + db->persist (d.o2); + db->persist (d.o3[0]); + db->persist (d.o4[0]); + db->persist (d); + t.commit (); + } + + // Load. + // + { + session s; + + transaction t (db->begin ()); + object1* p1 (db->load<object1> (d.o1->id)); + object2* p2 (db->load<object2> (d.o2->id)); + object3* p3 (db->load<object3> (d.o3[0]->id)); + object4* p4 (db->load<object4> (d.o4[0]->id)); + t.commit (); + + assert (p1->d->num == d.num); + assert (p2->d[0]->num == d.num); + assert (p3->d[0]->num == d.num); + assert (p4->d->num == d.num); + delete p1->d; + } + + // Query. + // + { + typedef odb::query<object1> query; + typedef odb::result<object1> result; + + session s; + transaction t (db->begin ()); + + result r (db->query<object1> (query::d->num == d.num)); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e && i->d->num == d.num); + delete i.load ()->d; + assert (++i == e); + t.commit (); + } + + { + typedef odb::query<object4> query; + typedef odb::result<object4> result; + + session s; + transaction t (db->begin ()); + + result r (db->query<object4> (query::d->num == d.num)); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e && i->d->num == d.num); + delete i.load ()->d; + assert (++i == e); + t.commit (); + } + } + + // Test 15: LOB/long data and polymorphism. + // + { + using namespace test15; + + const char data[] = "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B"; + + derived d; + d.blob.assign (data, data + sizeof (data)); + + // Persist. + // + { + transaction t (db->begin ()); + base* b (&d); + db->persist (b); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + + unique_ptr<base> pb (db->load<base> (d.id)); + t.commit (); + + derived* pd (dynamic_cast<derived*> (pb.get ())); + assert (pd != 0 && pd->blob == d.blob); + } + + // Query. + // + { + typedef odb::result<base> result; + + transaction t (db->begin ()); + + result r (db->query<base> ()); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e); + + derived* pd (dynamic_cast<derived*> (&*i)); + assert (pd != 0 && pd->blob == d.blob); + + assert (++i == e); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/inheritance/polymorphism/test1.hxx b/odb-tests/common/inheritance/polymorphism/test1.hxx new file mode 100644 index 0000000..7f598de --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test1.hxx @@ -0,0 +1,115 @@ +// file : common/inheritance/polymorphism/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#include <string> +#include <vector> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test basic polymorphism functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () {} + root () {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id column("object_id") + unsigned long id; + + unsigned long num; + std::vector<std::string> strs; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id == r.id && num == r.num && strs == r.strs; + } + }; + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return root::compare (r, false) && str == b.str && nums == b.nums; + } + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived)) + return false; + + const derived& d (static_cast<const derived&> (r)); + return base::compare (r, false) && dnum == d.dnum && dstr == d.dstr; + } + }; + + // Views. + // + #pragma db view object(root) + struct root_view + { + //#pragma db column(root::typeid_) + std::string typeid_; // @@ tmp + + unsigned long num; + }; + + #pragma db view object(base = b) + struct base_view + { + unsigned long id; + unsigned long num; + std::string str; + }; + + #pragma db view object(derived) + struct derived_view + { + unsigned long num; + std::string str; + unsigned long dnum; + std::string dstr; + }; +} + +#endif // TEST1_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test10.hxx b/odb-tests/common/inheritance/polymorphism/test10.hxx new file mode 100644 index 0000000..63673a1 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test10.hxx @@ -0,0 +1,78 @@ +// file : common/inheritance/polymorphism/test10.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST10_HXX +#define TEST10_HXX + +#include <typeinfo> + +#include <odb/core.hxx> + +// Test empty polymorphic classes. +// +#pragma db namespace table("t10_") +namespace test10 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () = 0; // Auto-abstract. + root () {} + root (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id == r.id; + } + }; + + inline root:: + ~root () {} + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, unsigned long n): root (i), num (n) {} + + unsigned long num; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return root::compare (r, false) && num == b.num; + } + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n): base (i, n) {} + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived)) + return false; + + return base::compare (r, false); + } + }; +} + +#endif // TEST10_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test11.hxx b/odb-tests/common/inheritance/polymorphism/test11.hxx new file mode 100644 index 0000000..2d38a6c --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test11.hxx @@ -0,0 +1,78 @@ +// file : common/inheritance/polymorphism/test11.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST11_HXX +#define TEST11_HXX + +#include <string> +#include <vector> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test mixing reuse and polymorphic inheritance. +// +#pragma db namespace table("t11_") +namespace test11 +{ + #pragma db object abstract + struct root + { + root () {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + std::vector<std::string> strs; + }; + + #pragma db object polymorphic + struct base: root + { + virtual ~base () {} + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const base& b, bool tc = true) const + { + if (tc && typeid (b) != typeid (base)) + return false; + + return id == b.id && num == b.num && strs == b.strs && + str == b.str && nums == b.nums; + } + }; + + inline bool + operator== (const base& x, const base& y) {return x.compare (y);} + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + virtual bool + compare (const base& b, bool tc = true) const + { + if (tc && typeid (b) != typeid (derived)) + return false; + + const derived& d (static_cast<const derived&> (b)); + return base::compare (b, false) && dnum == d.dnum && dstr == d.dstr; + } + }; +} + +#endif // TEST11_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test12.hxx b/odb-tests/common/inheritance/polymorphism/test12.hxx new file mode 100644 index 0000000..85000ac --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test12.hxx @@ -0,0 +1,79 @@ +// file : common/inheritance/polymorphism/test12.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST12_HXX +#define TEST12_HXX + +#include <typeinfo> + +#include <odb/core.hxx> + +// Test polymorphic classes with private auto id. +// +#pragma db namespace table("t12_") +namespace test12 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () = 0; // Auto-abstract. + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id_ == r.id_; + } + + unsigned long id () const {return id_;} + void id (unsigned long id) {id_ = id;} + private: + #pragma db id auto access(id) + unsigned long id_; + }; + + inline root:: + ~root () {} + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + base () {} + base (unsigned long n): num (n) {} + + unsigned long num; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return root::compare (r, false) && num == b.num; + } + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long n): base (n) {} + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived)) + return false; + + return base::compare (r, false); + } + }; +} + +#endif // TEST12_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test13.hxx b/odb-tests/common/inheritance/polymorphism/test13.hxx new file mode 100644 index 0000000..3240a9a --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test13.hxx @@ -0,0 +1,46 @@ +// file : common/inheritance/polymorphism/test13.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST13_HXX +#define TEST13_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +// Test polymorphic derived without any non-container data members (which +// results in an empty SELECT statement). +// +#pragma db namespace table("t13_") +namespace test13 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () {} + + #pragma db id auto + unsigned long id; + }; + + #pragma db object + struct base: root + { + std::vector<int> nums; + }; + + #pragma db object + struct derived: base + { + std::vector<std::string> strs; + }; + + #pragma db object + struct base1: root + { + // Nothing. + }; +} + +#endif // TEST13_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test14.hxx b/odb-tests/common/inheritance/polymorphism/test14.hxx new file mode 100644 index 0000000..1050861 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test14.hxx @@ -0,0 +1,99 @@ +// file : common/inheritance/polymorphism/test14.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST14_HXX +#define TEST14_HXX + +#include <vector> + +#include <odb/core.hxx> + +// Test inverse pointer in polymorphic base. +// +#pragma db namespace table("t14_") +namespace test14 +{ + struct object1; + struct object2; + struct object3; + struct object4; + + #pragma db object polymorphic session + struct base + { + virtual ~base (); + + #pragma db id auto + unsigned long id; + + object1* o1; + object2* o2; + std::vector<object3*> o3; + std::vector<object4*> o4; + }; + + #pragma db object + struct derived: base + { + unsigned long num; + }; + + // one-to-one(i) + // + #pragma db object session + struct object1 + { + #pragma db id auto + unsigned long id; + + #pragma db inverse(o1) + derived* d; + }; + + // one-to-many(i) + // + #pragma db object session + struct object2 + { + #pragma db id auto + unsigned long id; + + #pragma db inverse(o2) + std::vector<derived*> d; + }; + + // many-to-many(i) + // + #pragma db object session + struct object3 + { + #pragma db id auto + unsigned long id; + + #pragma db inverse(o3) + std::vector<derived*> d; + }; + + // many-to-one(i) + // + #pragma db object session + struct object4 + { + #pragma db id auto + unsigned long id; + + #pragma db inverse(o4) + derived* d; + }; + + inline base:: + ~base () + { + delete o1; + delete o2; + delete o3[0]; + delete o4[0]; + } +} + +#endif // TEST14_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test15.hxx b/odb-tests/common/inheritance/polymorphism/test15.hxx new file mode 100644 index 0000000..5799ace --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test15.hxx @@ -0,0 +1,44 @@ +// file : common/inheritance/polymorphism/test15.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST15_HXX +#define TEST15_HXX + +#include <vector> + +#include <odb/core.hxx> + +#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 LOB/long data and polymorphism. +// +#pragma db namespace table("t15_") +namespace test15 +{ + #pragma db object polymorphic + struct base + { + virtual ~base () {} + + #pragma db id auto + unsigned long id; + }; + + #pragma db object + struct derived: base + { + #pragma db type(BLOB_TYPE) + std::vector<char> blob; + }; +} + +#endif // TEST15_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test2.hxx b/odb-tests/common/inheritance/polymorphism/test2.hxx new file mode 100644 index 0000000..9890e02 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test2.hxx @@ -0,0 +1,105 @@ +// file : common/inheritance/polymorphism/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> +#include <odb/lazy-ptr.hxx> + +// Test inverse object pointers in polymorhic bases. +// +#pragma db namespace table("t2_") +namespace test2 +{ + struct root; + + #pragma db object + struct root_pointer + { + root_pointer (root* r = 0): p (r) {} + + #pragma db id auto + unsigned long id; + + root* p; + }; + + #pragma db object polymorphic + struct root + { + virtual ~root () {} + root () {} + root (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + #pragma db inverse(p) + odb::lazy_ptr<root_pointer> rp; + }; + + struct base; + + #pragma db object + struct base_pointer + { + base_pointer (base* b = 0) {if (b != 0) vp.push_back (b);} + + #pragma db id auto + unsigned long id; + + std::vector<base*> vp; + }; + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, const std::string& s): root (i), str (s) {} + + std::string str; + + #pragma db inverse(vp) + odb::lazy_ptr<base_pointer> bp; + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, const std::string& s, unsigned long n) + : base (i, s), num (n) {} + + unsigned long num; + }; + + // Views. + // + #pragma db view object(root_pointer = rp) object(root) + struct root_view + { + #pragma db column(rp::id) + unsigned long rp_id; + + #pragma db column(root::id) + unsigned long r_id; + }; + + #pragma db view object(base_pointer) object(base = b) + struct base_view + { + #pragma db column(base_pointer::id) + unsigned long bp_id; + + #pragma db column(b::id) + unsigned long b_id; + + std::string str; + }; +} + +#endif // TEST2_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test3.hxx b/odb-tests/common/inheritance/polymorphism/test3.hxx new file mode 100644 index 0000000..fd68f24 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test3.hxx @@ -0,0 +1,146 @@ +// file : common/inheritance/polymorphism/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#include <string> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test delayed loading. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () {} + root () {} + root (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id == r.id; + } + }; + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + virtual ~base () {delete rptr;} + base (): rptr (0) {} + base (unsigned long i, unsigned long n): root (i), num (n), rptr (0) {} + + unsigned long num; + root* rptr; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return + root::compare (r, false) && + num == b.num && + ((rptr == 0 && b.rptr == 0) || rptr->compare (*b.rptr)); + } + }; + + #pragma db object + struct derived: base + { + virtual ~derived () {delete bptr;} + derived (): bptr (0) {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n), str (s), bptr (0) {} + + std::string str; + base* bptr; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived)) + return false; + + const derived& d (static_cast<const derived&> (r)); + return + base::compare (r, false) && + str == d.str && + ((bptr == 0 && d.bptr == 0) || bptr->compare (*d.bptr)); + } + }; + + // Views. + // + #pragma db view object(base) object(root = r) + struct base_view + { + #pragma db column(base::id) + unsigned long b_id; + + #pragma db column(r::id) + unsigned long r_id; + + unsigned long num; + }; + + #pragma db view \ + object(derived = d) \ + object(base = b) \ + object(root = r: d::rptr) + struct derived_view + { + #pragma db column(d::id) + unsigned long d_id; + + #pragma db column(b::id) + unsigned long b_id; + + #pragma db column(r::id) + unsigned long r_id; + + #pragma db column(d::num) + unsigned long d_num; + + #pragma db column(b::num) + unsigned long b_num; + + std::string str; + }; + + // This is an example of a pathological case, where the right-hand-side + // of the join condition comes from one of the bases. As a result, we + // join the base table first, which means we will get both bases and + // derived objects instead of just derived. + // + //#pragma db view object(root = r) object(derived = d) + #pragma db view object(derived = d) object(root = r) + struct root_view + { + #pragma db column(r::id) + unsigned long r_id; + + #pragma db column(d::id) + unsigned long d_id; + + std::string str; + }; +} + +#endif // TEST3_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test4.hxx b/odb-tests/common/inheritance/polymorphism/test4.hxx new file mode 100644 index 0000000..148c53c --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test4.hxx @@ -0,0 +1,84 @@ +// file : common/inheritance/polymorphism/test4.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST4_HXX +#define TEST4_HXX + +#include <string> + +#include <odb/core.hxx> + +// Test views. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db object polymorphic + struct root1 + { + virtual ~root1 () {} + root1 () {} + root1 (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + }; + + #pragma db object + struct base1: root1 + { + base1 () {} + base1 (unsigned long i, unsigned long n): root1 (i), num (n) {} + + unsigned long num; + }; + + #pragma db object polymorphic + struct root2 + { + virtual ~root2 () {} + root2 () {} + root2 (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + }; + + #pragma db object + struct base2: root2 + { + base2 () {} + base2 (unsigned long i, unsigned long n, const std::string& s) + : root2 (i, n), str (s) {} + + std::string str; + }; + + // Test custom join condition. + // + #pragma db view object(base2) object(base1: base2::num == base1::num) + struct view1 + { + std::string str; + }; + + #pragma db view object(base2) + struct view2 + { + #pragma db column("min(" + base2::num + ")") + unsigned long min_num; + }; + + // Test custom join condition that uses object id. It cannot come + // from the base since the base table hasn't been join'ed yet. + // + #pragma db view object(base1) object(base2: base2::id == base1::id) + struct view3 + { + std::string str; + }; +} + +#endif // TEST4_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test5.hxx b/odb-tests/common/inheritance/polymorphism/test5.hxx new file mode 100644 index 0000000..172e7e8 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test5.hxx @@ -0,0 +1,92 @@ +// file : common/inheritance/polymorphism/test5.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST5_HXX +#define TEST5_HXX + +#include <string> +#include <vector> +#include <memory> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test polymorphism and optimistic concurrency. +// +#pragma db namespace table("t5_") +namespace test5 +{ + #pragma db object polymorphic optimistic pointer(std::unique_ptr) + struct root + { + virtual ~root () {} + root () {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + #pragma db version + unsigned long version; + + unsigned long num; + std::vector<std::string> strs; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id == r.id && version == r.version && + num == r.num && strs == r.strs; + } + }; + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return root::compare (r, false) && str == b.str && nums == b.nums; + } + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived)) + return false; + + const derived& d (static_cast<const derived&> (r)); + return base::compare (r, false) && dnum == d.dnum && dstr == d.dstr; + } + }; +} + +#endif // TEST5_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test6.hxx b/odb-tests/common/inheritance/polymorphism/test6.hxx new file mode 100644 index 0000000..b0f9a16 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test6.hxx @@ -0,0 +1,64 @@ +// file : common/inheritance/polymorphism/test6.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST6_HXX +#define TEST6_HXX + +#include <string> +#include <memory> + +#include <odb/core.hxx> +#include <odb/callback.hxx> + +// Test polymorphism and callbacks. +// +#pragma db namespace table("t6_") +namespace test6 +{ + #pragma db object polymorphic pointer(std::unique_ptr) + struct root + { + virtual ~root () {} + root (): id (0) {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + }; + + #pragma db object callback(db_callback) + struct base: root + { + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + + void + db_callback (odb::callback_event, odb::database&); + + void + db_callback (odb::callback_event, odb::database&) const; + }; + + #pragma db object callback(db_callback) + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + std::unique_ptr<root> ptr; + + void + db_callback (odb::callback_event, odb::database&) const; + }; +} + +#endif // TEST6_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test7.hxx b/odb-tests/common/inheritance/polymorphism/test7.hxx new file mode 100644 index 0000000..60da98e --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test7.hxx @@ -0,0 +1,54 @@ +// file : common/inheritance/polymorphism/test7.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST7_HXX +#define TEST7_HXX + +#include <string> +#include <memory> + +#include <odb/core.hxx> + +// Test polymorphism and object cache (session). +// +#pragma db namespace table("t7_") +namespace test7 +{ + using std::shared_ptr; + + #pragma db object polymorphic pointer(shared_ptr) session + struct root + { + virtual ~root () {} + root (): id (0) {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + }; + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + }; + + #pragma db object + struct derived: base + { + derived () {} + derived (unsigned long i, unsigned long n, const std::string& s) + : base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + }; +} + +#endif // TEST7_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test8.hxx b/odb-tests/common/inheritance/polymorphism/test8.hxx new file mode 100644 index 0000000..84b6688 --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test8.hxx @@ -0,0 +1,129 @@ +// file : common/inheritance/polymorphism/test8.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST8_HXX +#define TEST8_HXX + +#include <string> +#include <vector> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test polymorphism and abstract bases. +// +#pragma db namespace table("t8_") +namespace test8 +{ + #pragma db object polymorphic + struct root + { + virtual ~root () = 0; // Auto-abstract. + root () {} + root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + std::vector<std::string> strs; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (root)) + return false; + + return id == r.id && num == r.num && strs == r.strs; + } + }; + + inline root:: + ~root () {} + + inline bool + operator== (const root& x, const root& y) {return x.compare (y);} + + #pragma db object + struct base: root + { + base () {} + base (unsigned long i, unsigned long n, const std::string& s) + : root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (base)) + return false; + + const base& b (static_cast<const base&> (r)); + return root::compare (r, false) && str == b.str && nums == b.nums; + } + }; + + #pragma db object abstract + struct interm: base + { + interm () {} + interm (unsigned long i, unsigned long n, const std::string& s, bool b) + : base (i, n, s), bln (b) {} + + bool bln; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (interm)) + return false; + + const interm& i (static_cast<const interm&> (r)); + return base::compare (r, false) && bln == i.bln; + } + }; + + #pragma db object + struct derived1: interm + { + derived1 () {} + derived1 (unsigned long i, unsigned long n, const std::string& s, bool b) + : interm (i, n, s, b), dnum (n + 1) {} + + unsigned long dnum; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived1)) + return false; + + const derived1& d (static_cast<const derived1&> (r)); + return interm::compare (r, false) && dnum == d.dnum; + } + }; + + #pragma db object + struct derived2: interm + { + derived2 () {} + derived2 (unsigned long i, unsigned long n, const std::string& s, bool b) + : interm (i, n, s, b), dstr (s + 'd') {} + + std::string dstr; + + virtual bool + compare (const root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (derived2)) + return false; + + const derived2& d (static_cast<const derived2&> (r)); + return interm::compare (r, false) && dstr == d.dstr; + } + }; +} + +#endif // TEST8_HXX diff --git a/odb-tests/common/inheritance/polymorphism/test9.hxx b/odb-tests/common/inheritance/polymorphism/test9.hxx new file mode 100644 index 0000000..cdc97ae --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/test9.hxx @@ -0,0 +1,161 @@ +// file : common/inheritance/polymorphism/test9.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST9_HXX +#define TEST9_HXX + +#include <string> +#include <vector> +#include <typeinfo> + +#include <odb/core.hxx> + +// Test polymorphism and readonly classes. +// +#pragma db namespace table("t9_") +namespace test9 +{ + // + // ro_root, rw_base, ro_derived + // + #pragma db object polymorphic readonly + struct ro_root + { + virtual ~ro_root () {} + ro_root () {} + ro_root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + std::vector<std::string> strs; + + virtual bool + compare (const ro_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (ro_root)) + return false; + + return id == r.id && num == r.num && strs == r.strs; + } + }; + + inline bool + operator== (const ro_root& x, const ro_root& y) {return x.compare (y);} + + #pragma db object + struct rw_base: ro_root + { + rw_base () {} + rw_base (unsigned long i, unsigned long n, const std::string& s) + : ro_root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const ro_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (rw_base)) + return false; + + const rw_base& b (static_cast<const rw_base&> (r)); + return ro_root::compare (r, false) && str == b.str && nums == b.nums; + } + }; + + #pragma db object readonly + struct ro_derived: rw_base + { + ro_derived () {} + ro_derived (unsigned long i, unsigned long n, const std::string& s) + : rw_base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + virtual bool + compare (const ro_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (ro_derived)) + return false; + + const ro_derived& d (static_cast<const ro_derived&> (r)); + return rw_base::compare (r, false) && dnum == d.dnum && dstr == d.dstr; + } + }; + + // + // rw_root, ro_base, rw_derived + // + #pragma db object polymorphic + struct rw_root + { + virtual ~rw_root () {} + rw_root () {} + rw_root (unsigned long i, unsigned long n): id (i), num (n) {} + + #pragma db id + unsigned long id; + + unsigned long num; + std::vector<std::string> strs; + + virtual bool + compare (const rw_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (rw_root)) + return false; + + return id == r.id && num == r.num && strs == r.strs; + } + }; + + inline bool + operator== (const rw_root& x, const rw_root& y) {return x.compare (y);} + + #pragma db object readonly + struct ro_base: rw_root + { + ro_base () {} + ro_base (unsigned long i, unsigned long n, const std::string& s) + : rw_root (i, n), str (s) {} + + std::string str; + std::vector<unsigned long> nums; + + virtual bool + compare (const rw_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (ro_base)) + return false; + + const ro_base& b (static_cast<const ro_base&> (r)); + return rw_root::compare (r, false) && str == b.str && nums == b.nums; + } + }; + + #pragma db object + struct rw_derived: ro_base + { + rw_derived () {} + rw_derived (unsigned long i, unsigned long n, const std::string& s) + : ro_base (i, n, s), dnum (n + 1), dstr (s + 'd') {} + + unsigned long dnum; + std::string dstr; + + virtual bool + compare (const rw_root& r, bool tc = true) const + { + if (tc && typeid (r) != typeid (rw_derived)) + return false; + + const rw_derived& d (static_cast<const rw_derived&> (r)); + return ro_base::compare (r, false) && dnum == d.dnum && dstr == d.dstr; + } + }; +} + +#endif // TEST9_HXX diff --git a/odb-tests/common/inheritance/polymorphism/testscript b/odb-tests/common/inheritance/polymorphism/testscript new file mode 100644 index 0000000..adde79b --- /dev/null +++ b/odb-tests/common/inheritance/polymorphism/testscript @@ -0,0 +1,106 @@ +# file : common/inheritance/polymorphism/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + ++cat <<EOI >=output + base pre_persist 1 const + base post_persist 1 const + derived pre_persist 2 const + derived post_persist 2 const + base pre_load 0 + base post_load 1 + derived pre_load 0 const + derived post_load 2 const + base pre_load 1 + base post_load 1 + derived pre_load 2 const + derived post_load 2 const + base pre_load 1 + base post_load 1 + derived pre_load 2 const + derived post_load 2 const + base pre_update 1 const + base post_update 1 const + derived pre_update 2 const + derived post_update 2 const + base pre_load 0 + base post_load 1 + derived pre_load 0 const + derived post_load 2 const + base pre_erase 1 const + base post_erase 1 const + derived pre_erase 2 const + derived post_erase 2 const + derived pre_persist 3 const + derived post_persist 3 const + derived pre_persist 4 const + derived post_persist 4 const + derived pre_load 0 const + derived pre_load 0 const + derived post_load 4 const + derived post_load 3 const + EOI + +test.redirects += >>>../output + +: mysql +: +if $mysql +{ + .include ../../../mysql-schema.testscript + + for s: $schemas + cat $out_base/"$s"($multi ? '-mysql' : '').sql | $create_schema_cmd + end; + + $* ($multi ? 'mysql' : ) $mysql_options +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql-schema.testscript + + for s: $schemas + $create_schema_cmd -f $out_base/"$s"($multi ? '-pgsql' : '').sql + end; + + $* ($multi ? 'pgsql' : ) $pgsql_options +} + +: oracle +: +if $oracle +{ + .include ../../../oracle-schema.testscript + + for s: $schemas + $create_schema_cmd "@$out_base/$s"($multi ? '-oracle' : '').sql + end; + + $* ($multi ? 'oracle' : ) $oracle_options +} + +: mssql +: +if $mssql +{ + .include ../../../mssql-schema.testscript + + for s: $schemas + $create_schema_cmd -i $out_base/"$s"($multi ? '-mssql' : '').sql + end; + + $* ($multi ? 'mssql' : ) $mssql_options +} diff --git a/odb-tests/common/inheritance/reuse/buildfile b/odb-tests/common/inheritance/reuse/buildfile new file mode 100644 index 0000000..0b7f944 --- /dev/null +++ b/odb-tests/common/inheritance/reuse/buildfile @@ -0,0 +1,53 @@ +# file : common/inheritance/reuse/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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). +# +# Note that the table prefix inhrt_r_, which we would normally use for this +# test, doesn't work well for Oracle. In this case ODB ends up with the +# following error: +# +# test.hxx:16:30: error: index name 'inhrt_r_empty_object_comp_bool' conflicts with an already defined index name +# test.hxx:16:30: info: index name 'inhrt_r_empty_object_comp_bool' is truncated 'inhrt_r_empty_object_comp_bools_index_i' +# test.hxx:16:30: info: conflicting index is defined here +# test.hxx:16:30: info: conflicting index name 'inhrt_r_empty_object_comp_bool' is truncated 'inhrt_r_empty_object_comp_bools_object_id_i' +# test.hxx:16:30: info: use #pragma db index to change one of the names +# +# Thus, we shorten the prefix to just i_r_. +# +odb_options = --table-prefix i_r_ \ + --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/inheritance/reuse/driver.cxx b/odb-tests/common/inheritance/reuse/driver.cxx new file mode 100644 index 0000000..e6122bb --- /dev/null +++ b/odb-tests/common/inheritance/reuse/driver.cxx @@ -0,0 +1,237 @@ +// file : common/inheritance/reuse/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test reuse object inheritance. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + base b; + b.comp_.bools.push_back (true); + b.comp_.obools.push_back (true); + b.comp_.num = 10; + b.comp_.str = "comp bbb"; + b.comp_.nums.push_back (101); + b.comp_.nums.push_back (102); + b.comp_.onums.push_back (101); + b.comp_.onums.push_back (102); + b.num_ = 0; + b.str_ = "bbb"; + b.strs_.push_back ("bbb one"); + b.strs_.push_back ("bbb two"); + b.ostrs_.push_back ("bbb one"); + b.ostrs_.push_back ("bbb two"); + + object1 o1; + o1.comp_.bools.push_back (false); + o1.comp_.obools.push_back (false); + o1.comp_.num = 11; + o1.comp_.str = "comp o1o1o1"; + o1.comp_.nums.push_back (111); + o1.comp_.nums.push_back (112); + o1.comp_.onums.push_back (111); + o1.comp_.onums.push_back (112); + static_cast<base&> (o1).num_ = 1; + o1.num1_ = 21; + o1.str_ = "base o1o1o1"; + o1.strs_.push_back ("base o1o1o1 one"); + o1.strs_.push_back ("base o1o1o1 two"); + o1.ostrs_.push_back ("base o1o1o1 one"); + o1.ostrs_.push_back ("base o1o1o1 two"); + + object2 o2; + o2.comp_.bools.push_back (true); + o2.comp_.bools.push_back (false); + o2.comp_.obools.push_back (true); + o2.comp_.obools.push_back (false); + o2.comp_.num = 12; + o2.comp_.str = "comp o2o2o2"; + o2.comp_.nums.push_back (121); + o2.comp_.nums.push_back (122); + o2.comp_.onums.push_back (121); + o2.comp_.onums.push_back (122); + o2.num_ = 2; + static_cast<base&> (o2).str_ = "base o2o2o2"; + o2.str_ = "o2o2o2"; + o2.strs_.push_back ("base o2o2o2 one"); + o2.strs_.push_back ("base o2o2o2 two"); + o2.ostrs_.push_back ("base o2o2o2 one"); + o2.ostrs_.push_back ("base o2o2o2 two"); + + object3 o3; + o3.comp_.bools.push_back (false); + o3.comp_.bools.push_back (false); + o3.comp_.obools.push_back (false); + o3.comp_.obools.push_back (false); + o3.comp_.num = 13; + o3.comp_.str = "comp o3o3o3"; + o3.comp_.nums.push_back (131); + o3.comp_.nums.push_back (132); + o3.comp_.onums.push_back (131); + o3.comp_.onums.push_back (132); + o3.num_ = 3; + o3.str_ = "base o3o3o3"; + o3.strs_.push_back ("base o3o3o3 one"); + o3.strs_.push_back ("base o3o3o3 two"); + o3.ostrs_.push_back ("base o3o3o3 one"); + o3.ostrs_.push_back ("base o3o3o3 two"); + + reference r; + r.o1_ = &o1; + + empty_object e; + e.comp_.bools.push_back (true); + e.comp_.bools.push_back (true); + e.comp_.obools.push_back (true); + e.comp_.obools.push_back (true); + e.comp_.num = 14; + e.comp_.str = "comp eee"; + e.comp_.nums.push_back (141); + e.comp_.nums.push_back (142); + e.comp_.onums.push_back (141); + e.comp_.onums.push_back (142); + e.num_ = 4; + e.str_ = "base eee"; + e.strs_.push_back ("base eee one"); + e.strs_.push_back ("base eee two"); + e.ostrs_.push_back ("base eee one"); + e.ostrs_.push_back ("base eee two"); + + // persist + // + { + transaction t (db->begin ()); + db->persist (b); + db->persist (o1); + db->persist (o2); + db->persist (o3); + db->persist (r); + db->persist (e); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<base> lb (db->load<base> (b.id_)); + unique_ptr<object1> lo1 (db->load<object1> (o1.id_)); + unique_ptr<object2> lo2 (db->load<object2> (o2.id_)); + unique_ptr<object3> lo3 (db->load<object3> (o3.id_)); + unique_ptr<empty_object> le (db->load<empty_object> (e.id_)); + unique_ptr<reference> lr (db->load<reference> (r.id_)); + t.commit (); + + assert (b == *lb); + assert (o1 == *lo1); + assert (o2 == *lo2); + assert (o3 == *lo3); + assert (lr->o1_->id_ == r.o1_->id_); + assert (e == *le); + + delete lr->o1_; + } + + // update + // + { + transaction t (db->begin ()); + db->update (b); + db->update (o1); + db->update (o2); + db->update (o3); + db->update (r); + db->update (e); + t.commit (); + } + + // query + // + { + typedef odb::query<base> b_query; + typedef odb::query<object1> o1_query; + typedef odb::query<object2> o2_query; + typedef odb::query<reference> r_query; + + typedef odb::result<reference> r_result; + + transaction t (db->begin ()); + + assert (!db->query<base> (b_query::comp.num == 10).empty ()); + assert (!db->query<object1> (o1_query::num1 == 21).empty ()); + assert (!db->query<object2> (o2_query::num == 2).empty ()); + + // Query condition with hidden members. + // + assert ( + !db->query<object2> (o2_query::base::str == "base o2o2o2").empty ()); + + // Query condition with referenced composite member in base class. + // + { + r_result r (db->query<reference> (r_query::o1->comp.num == 11)); + assert (!r.empty ()); + delete r.begin ()->o1_; + } + + t.commit (); + } + + // views + // + { + typedef odb::query<object2_view> query; + typedef odb::result<object2_view> result; + + transaction t (db->begin ()); + + result r (db->query<object2_view> (query::num == o2.num_)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->num == o2.num_ && i->id == o2.id_ && i->str == o2.str_); + assert (++i == r.end ()); + + t.commit (); + } + + // erase + // + { + transaction t (db->begin ()); + db->erase (b); + db->erase (o1); + db->erase (o2); + db->erase (o3); + db->erase (r); + db->erase (e); + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/inheritance/reuse/test.hxx b/odb-tests/common/inheritance/reuse/test.hxx new file mode 100644 index 0000000..48f474f --- /dev/null +++ b/odb-tests/common/inheritance/reuse/test.hxx @@ -0,0 +1,163 @@ +// file : common/inheritance/reuse/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> +#include <odb/vector.hxx> + +#pragma db value +struct comp_base +{ + std::vector<unsigned char> bools; + odb::vector<unsigned char> obools; + + bool + operator== (const comp_base& y) const + { + return bools == y.bools && obools == y.obools; + } +}; + +#pragma db value +struct comp: comp_base +{ + unsigned int num; + std::string str; + + std::vector<unsigned int> nums; + odb::vector<unsigned int> onums; + + bool + operator== (const comp& y) const + { + return + static_cast<const comp_base&> (*this) == y && + num == y.num && + str == y.str && + nums == y.nums && + onums == y.onums; + } +}; + +#pragma db object abstract +struct abstract_base +{ + comp comp_; + + unsigned int num_; + std::string str_; + + std::vector<std::string> strs_; + odb::vector<std::string> ostrs_; + + bool + operator== (const abstract_base& y) const + { + return + comp_ == y.comp_ && + num_ == y.num_ && + str_ == y.str_ && + strs_ == y.strs_ && + ostrs_ == y.ostrs_; + } +}; + +#pragma db object +struct base: abstract_base +{ + #pragma db id auto + unsigned long id_; + + bool + operator== (const base& y) const + { + return id_ == y.id_ && static_cast<const abstract_base&> (*this) == y; + } +}; + +#pragma db object +struct object1: base +{ + unsigned int num1_; + + bool + operator== (const object1& y) const + { + return static_cast<const base&> (*this) == y && num1_ == y.num1_; + } +}; + +#pragma db object +struct object2: base +{ + #pragma db column("derived_str") + std::string str_; + + bool + operator== (const object2& y) const + { + return static_cast<const base&> (*this) == y && str_ == y.str_; + } +}; + +// Reference to derived object. +// +#pragma db object +struct reference +{ + #pragma db id auto + unsigned long id_; + + object1* o1_; +}; + +// Multiple inheritance. +// +#pragma db object abstract +struct id_base +{ + #pragma db id auto + unsigned long id_; + + bool + operator== (const id_base& y) const + { + return id_ == y.id_; + } +}; + +#pragma db object +struct object3: abstract_base, id_base +{ + bool + operator== (const object3& y) const + { + return + static_cast<const abstract_base&> (*this) == y && + static_cast<const id_base&> (*this) == y; + } +}; + +// Empty derived object. +// +#pragma db object +struct empty_object: base +{ +}; + +// View based on the derived object. +// +#pragma db view object(object2) +struct object2_view +{ + unsigned int num; // from abstract_base + unsigned long id; // from base + std::string str; // from object2, hides one from abstract_base +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/inheritance/reuse/testscript b/odb-tests/common/inheritance/reuse/testscript new file mode 100644 index 0000000..1bc20ea --- /dev/null +++ b/odb-tests/common/inheritance/reuse/testscript @@ -0,0 +1,53 @@ +# file : common/inheritance/reuse/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/inheritance/transient/buildfile b/odb-tests/common/inheritance/transient/buildfile new file mode 100644 index 0000000..1961abc --- /dev/null +++ b/odb-tests/common/inheritance/transient/buildfile @@ -0,0 +1,41 @@ +# file : common/inheritance/transient/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 inhrt_t_ \ + --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/inheritance/transient/driver.cxx b/odb-tests/common/inheritance/transient/driver.cxx new file mode 100644 index 0000000..1caae6c --- /dev/null +++ b/odb-tests/common/inheritance/transient/driver.cxx @@ -0,0 +1,80 @@ +// file : common/inheritance/transient/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test transient inheritance of objects, composite value types, and views. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + object o; + o.num = 1; + o.str = "abc"; + o.strs.push_back ("abc 1"); + o.strs.push_back ("abc 2"); + o.c.num = 11; + o.c.str = "comp abc"; + o.c.nums.push_back (111); + o.c.nums.push_back (112); + + // persist + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id_)); + t.commit (); + + assert (*p == o); + } + + // view + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + + result r (db->query<view> (query::id == o.id_)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->num == o.num && i->str == o.str); + assert (++i == r.end ()); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/inheritance/transient/test.hxx b/odb-tests/common/inheritance/transient/test.hxx new file mode 100644 index 0000000..394ee8f --- /dev/null +++ b/odb-tests/common/inheritance/transient/test.hxx @@ -0,0 +1,60 @@ +// file : common/inheritance/transient/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +struct object; + +struct base +{ + int n; + std::vector<std::string> v; + object* p; +}; + +#pragma db value +struct comp: base +{ + unsigned int num; + std::string str; + std::vector<unsigned int> nums; + + bool + operator== (const comp& y) const + { + return num == y.num && str == y.str && nums == y.nums; + } +}; + +#pragma db object +struct object: base +{ + #pragma db id auto + unsigned int id_; + + unsigned int num; + std::string str; + std::vector<std::string> strs; + comp c; + + bool + operator== (const object& y) const + { + return num == y.num && str == y.str && strs == y.strs && c == y.c; + } +}; + +#pragma db view object(object) +struct view: base +{ + unsigned int num; + std::string str; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/inheritance/transient/testscript b/odb-tests/common/inheritance/transient/testscript new file mode 100644 index 0000000..d15db56 --- /dev/null +++ b/odb-tests/common/inheritance/transient/testscript @@ -0,0 +1,53 @@ +# file : common/inheritance/transient/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/inverse/buildfile b/odb-tests/common/inverse/buildfile new file mode 100644 index 0000000..bea135b --- /dev/null +++ b/odb-tests/common/inverse/buildfile @@ -0,0 +1,45 @@ +# file : common/inverse/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_inverse_ \ + --generate-schema \ + --generate-query \ + --generate-session + +if $mssql + odb_options += --fkeys-deferrable-mode mssql:not_deferrable + +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/inverse/driver.cxx b/odb-tests/common/inverse/driver.cxx new file mode 100644 index 0000000..842438e --- /dev/null +++ b/odb-tests/common/inverse/driver.cxx @@ -0,0 +1,502 @@ +// file : common/inverse/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test bidirectional relationships with inverse sides. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test raw pointers. + // + { + using namespace test1; + + obj1_ptr o1_1 (new obj1); + obj1_ptr o1_2 (new obj1); + obj2_ptr o2 (new obj2); + obj3_ptr o3_1 (new obj3); + obj3_ptr o3_2 (new obj3); + obj4_ptr o4_1 (new obj4); + obj4_ptr o4_2 (new obj4); + obj5_ptr o5_1 (new obj5); + obj5_ptr o5_2 (new obj5); + obj5_ptr o5_3 (new obj5); + obj5_ptr o5_4 (new obj5); + + o1_1->id = "obj1 1"; + o1_1->o2 = o2; + o1_1->o3.insert (o3_1); + o1_1->o3.insert (o3_2); + o1_1->o4 = o4_1; + o1_1->o5.insert (o5_1); + o1_1->o5.insert (o5_2); + + o1_2->id = "obj1 2"; + o1_2->o2 = 0; + o1_2->o3.clear (); + o1_2->o4 = o4_2; + o1_2->o5.insert (o5_3); + o1_2->o5.insert (o5_4); + + o2->str = "obj2"; + o2->o1 = o1_1; + + o3_1->str = "obj3 1"; + o3_1->o1 = o1_1; + + o3_2->str = "obj3 2"; + o3_2->o1 = o1_1; + + o4_1->str = "obj4 1"; + o4_1->o1.insert (o1_1); + + o4_2->str = "obj4 2"; + o4_2->o1.insert (o1_2); + + o5_1->str = "obj5 1"; + o5_1->o1.insert (o1_1); + + o5_2->str = "obj5 2"; + o5_2->o1.insert (o1_1); + + o5_3->str = "obj5 3"; + o5_3->o1.insert (o1_2); + + o5_4->str = "obj5 4"; + o5_4->o1.insert (o1_2); + + // persist + // + { + transaction t (db->begin ()); + + // objN come before obj1 to get object id assigned. + // + db->persist (o5_1); + db->persist (o5_2); + db->persist (o5_3); + db->persist (o5_4); + db->persist (o4_1); + db->persist (o4_2); + db->persist (o3_1); + db->persist (o3_2); + db->persist (o2); + db->persist (o1_1); + db->persist (o1_2); + + t.commit (); + } + + // load + // + { + session s; + transaction t (db->begin ()); + obj2_ptr x2 (db->load<obj2> (o2->id)); + obj3_ptr x3_1 (db->load<obj3> (o3_1->id)); + obj3_ptr x3_2 (db->load<obj3> (o3_2->id)); + obj4_ptr x4_1 (db->load<obj4> (o4_1->id)); + obj4_ptr x4_2 (db->load<obj4> (o4_2->id)); + obj5_ptr x5_1 (db->load<obj5> (o5_1->id)); + obj5_ptr x5_2 (db->load<obj5> (o5_2->id)); + obj5_ptr x5_3 (db->load<obj5> (o5_3->id)); + obj5_ptr x5_4 (db->load<obj5> (o5_4->id)); + t.commit (); + + assert (x2->str == o2->str); + assert (x2->o1->id == o1_1->id); + assert (x2->o1->o2 == x2); + + assert (x3_1->str == o3_1->str); + assert (x3_2->str == o3_2->str); + assert (x3_1->o1 == x3_2->o1); + assert (x3_1->o1->id == o1_1->id); + assert (x3_1->o1->o3.find (x3_1) != x3_1->o1->o3.end ()); + assert (x3_1->o1->o3.find (x3_2) != x3_1->o1->o3.end ()); + + assert (x4_1->str == o4_1->str); + assert (x4_2->str == o4_2->str); + assert ((*x4_1->o1.begin ())->id == o1_1->id); + assert ((*x4_2->o1.begin ())->id == o1_2->id); + assert ((*x4_1->o1.begin ())->o4 == x4_1); + assert ((*x4_2->o1.begin ())->o4 == x4_2); + + assert (x5_1->str == o5_1->str); + assert (x5_2->str == o5_2->str); + assert ((*x5_1->o1.begin ())->id == o1_1->id); + assert ((*x5_2->o1.begin ())->id == o1_1->id); + assert ((*x5_3->o1.begin ())->id == o1_2->id); + assert ((*x5_4->o1.begin ())->id == o1_2->id); + assert ((*x5_1->o1.begin ())->o5.find (x5_1) != + (*x5_1->o1.begin ())->o5.end ()); + assert ((*x5_2->o1.begin ())->o5.find (x5_2) != + (*x5_2->o1.begin ())->o5.end ()); + assert ((*x5_3->o1.begin ())->o5.find (x5_3) != + (*x5_3->o1.begin ())->o5.end ()); + assert ((*x5_4->o1.begin ())->o5.find (x5_4) != + (*x5_4->o1.begin ())->o5.end ()); + + delete *x4_1->o1.begin (); + delete *x4_2->o1.begin (); + } + + // query + // + { + // one(i)-to-one + // + typedef odb::query<obj2> query; + typedef odb::result<obj2> result; + + session s; + transaction t (db->begin ()); + + result r (db->query<obj2> (query::o1->id == "obj1 1")); + assert (!r.empty ()); + assert (r.begin ()->id == o2->id); + assert (r.begin ()->o1->id == o1_1->id); + assert (size (r) == 1); + + t.commit (); + } + + { + // one(i)-to-many + // + typedef odb::query<obj3> query; + typedef odb::result<obj3> result; + + session s; + transaction t (db->begin ()); + + result r (db->query<obj3> (query::o1->id == "obj1 1")); + size_t n (0); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + assert (i->id == o3_1->id || i->id == o3_2->id); + assert (i->o1->id == o1_1->id); + n++; + } + + assert (n == 2); + + t.commit (); + } + + delete o1_1; + delete o1_2; + } + + // Test shared_ptr/weak_ptr. + // + { + using namespace test2; + + obj1_ptr o1_1 (new obj1); + obj1_ptr o1_2 (new obj1); + obj2_ptr o2 (new obj2); + obj3_ptr o3_1 (new obj3); + obj3_ptr o3_2 (new obj3); + obj4_ptr o4 (new obj4); + obj5_ptr o5_1 (new obj5); + obj5_ptr o5_2 (new obj5); + + o1_1->id = "obj1 1"; + o1_1->o2 = o2; + o1_1->o3.push_back (o3_1); + o1_1->o3.push_back (o3_2); + o1_1->o4 = o4; + o1_1->o5.push_back (o5_1); + o1_1->o5.push_back (o5_2); + + o1_2->id = "obj1 2"; + o1_2->o2 = obj2_ptr (); + o1_2->o3.clear (); + o1_2->o4 = o4; + o1_2->o5.push_back (o5_1); + + o2->str = "obj2"; + o2->o1 = o1_1; + + o3_1->str = "obj3 1"; + o3_1->o1 = o1_1; + + o3_2->str = "obj3 3"; + o3_2->o1 = o1_1; + + o4->str = "obj4"; + o4->o1.push_back (o1_1); + o4->o1.push_back (o1_2); + + o5_1->str = "obj5 1"; + o5_1->o1.push_back (o1_1); + o5_1->o1.push_back (o1_2); + + o5_2->str = "obj5 2"; + o5_2->o1.push_back (o1_1); + + // persist + // + { + transaction t (db->begin ()); + + // objN come before obj1 to get object id assigned. + // + db->persist (o5_1); + db->persist (o5_2); + db->persist (o4); + db->persist (o3_1); + db->persist (o3_2); + db->persist (o2); + db->persist (o1_1); + db->persist (o1_2); + + t.commit (); + } + + // load + // + { + session s; + transaction t (db->begin ()); + obj2_ptr x2 (db->load<obj2> (o2->id)); + obj3_ptr x3_1 (db->load<obj3> (o3_1->id)); + obj3_ptr x3_2 (db->load<obj3> (o3_2->id)); + obj4_ptr x4 (db->load<obj4> (o4->id)); + obj5_ptr x5_1 (db->load<obj5> (o5_1->id)); + obj5_ptr x5_2 (db->load<obj5> (o5_2->id)); + t.commit (); + + assert (x2->str == o2->str); + assert (x2->o1.lock ()->id == o1_1->id); + assert (x2->o1.lock ()->o2 == x2); + + assert (x3_1->str == o3_1->str); + assert (x3_2->str == o3_2->str); + assert (x3_1->o1.lock () == x3_2->o1.lock ()); + assert (x3_1->o1.lock ()->id == o1_1->id); + assert (x3_1->o1.lock ()->o3[0] == x3_1); + assert (x3_1->o1.lock ()->o3[1] == x3_2); + + { + assert (x4->str == o4->str); + + obj1_ptr t1 (x4->o1[0].lock ()), t2 (x4->o1[1].lock ()); + + assert (t1->id == o1_1->id || t2->id == o1_1->id); + assert (t1->id == o1_2->id || t2->id == o1_2->id); + } + + { + assert (x5_1->str == o5_1->str); + assert (x5_2->str == o5_2->str); + + obj1_ptr t1 (x5_1->o1[0].lock ()), t2 (x5_1->o1[1].lock ()), + t3 (x5_2->o1[0].lock ()); + + assert (t1->id == o1_1->id || t2->id == o1_1->id); + assert (t1->id == o1_2->id || t2->id == o1_2->id); + assert (t3->id == o1_1->id); + } + } + } + + // Test inverse based on points_to. + // + { + using namespace test3; + + { + obj1 o1 (1, 2); + o1.o2 = new obj2; + + { + transaction t (db->begin ()); + + o1.o2->o1 = db->persist (o1); + db->persist (o1.o2); + + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<obj1> p (db->load<obj1> (o1.id)); + assert (p->o2->id == o1.o2->id); + + t.commit (); + } + + { + typedef odb::query<obj1> query; + + transaction t (db->begin ()); + + unique_ptr<obj1> p (db->query_one<obj1> (query::o2->o1.i == o1.id.i && + query::o2->o1.j == o1.id.j)); + assert (p->o2->id == o1.o2->id); + + t.commit (); + } + } + + { + obj3 o3; + o3.o4.push_back (new obj4); + o3.o4.push_back (new obj4); + + { + transaction t (db->begin ()); + + o3.o4[0]->o3 = o3.o4[1]->o3 = db->persist (o3); + db->persist (o3.o4[0]); + db->persist (o3.o4[1]); + + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<obj3> p (db->load<obj3> (o3.id)); + assert (p->o4[0]->id == o3.o4[0]->id); + assert (p->o4[1]->id == o3.o4[1]->id); + + t.commit (); + } + + { + typedef odb::query<obj3> query; + + transaction t (db->begin ()); + + unique_ptr<obj3> p (db->query_one<obj3> (query::id == o3.id)); + assert (p->o4[0]->id == o3.o4[0]->id); + assert (p->o4[1]->id == o3.o4[1]->id); + + t.commit (); + } + } + } + + // Test inverse with nested data members. + // + { + using namespace test4; + + { + obj1 o1; + o1.o2 = new obj2; + + { + transaction t (db->begin ()); + + o1.o2->id.i = db->persist (o1); + o1.o2->id.j = 123; + db->persist (o1.o2); + + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<obj1> p (db->load<obj1> (o1.id)); + assert (p->o2->id.i == o1.o2->id.i && p->o2->id.j == o1.o2->id.j); + + t.commit (); + } + + { + typedef odb::query<obj1> query; + + transaction t (db->begin ()); + + unique_ptr<obj1> p (db->query_one<obj1> ( + query::o2->id.i == o1.o2->id.i && + query::o2->id.j == o1.o2->id.j)); + assert (p->o2->id.i == o1.o2->id.i && p->o2->id.j == o1.o2->id.j); + + t.commit (); + } + } + + { + obj3 o3; + o3.o4.push_back (new obj4); + o3.o4.push_back (new obj4); + + { + transaction t (db->begin ()); + + o3.o4[0]->id.i = o3.o4[1]->id.i = db->persist (o3); + o3.o4[0]->id.j = 123; + o3.o4[1]->id.j = 234; + db->persist (o3.o4[0]); + db->persist (o3.o4[1]); + + t.commit (); + } + + { + transaction t (db->begin ()); + + unique_ptr<obj3> p (db->load<obj3> (o3.id)); + assert (p->o4[0]->id.i == o3.o4[0]->id.i && + p->o4[0]->id.j == o3.o4[0]->id.j); + + assert (p->o4[1]->id.i == o3.o4[1]->id.i && + p->o4[1]->id.j == o3.o4[1]->id.j); + + t.commit (); + } + + { + typedef odb::query<obj3> query; + + transaction t (db->begin ()); + + unique_ptr<obj3> p (db->query_one<obj3> (query::id == o3.id)); + + assert (p->o4[0]->id.i == o3.o4[0]->id.i && + p->o4[0]->id.j == o3.o4[0]->id.j); + + assert (p->o4[1]->id.i == o3.o4[1]->id.i && + p->o4[1]->id.j == o3.o4[1]->id.j); + + t.commit (); + } + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/inverse/test.hxx b/odb-tests/common/inverse/test.hxx new file mode 100644 index 0000000..a7b8678 --- /dev/null +++ b/odb-tests/common/inverse/test.hxx @@ -0,0 +1,391 @@ +// file : common/inverse/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <set> +#include <vector> +#include <string> +#include <memory> + +#include <odb/core.hxx> + +// Test raw pointers. +// +#pragma db namespace table("t1_") +namespace test1 +{ + struct obj1; + struct obj2; + struct obj3; + struct obj4; + struct obj5; + + typedef obj1* obj1_ptr; + typedef obj2* obj2_ptr; + typedef obj3* obj3_ptr; + typedef obj4* obj4_ptr; + typedef obj5* obj5_ptr; + + typedef std::set<obj1_ptr> obj1_ptr_set; + typedef std::set<obj3_ptr> obj3_ptr_set; + typedef std::set<obj5_ptr> obj5_ptr_set; + + #pragma db object + struct obj1 + { + obj1 (): o2 (0), o4 (0) {} + ~obj1 (); + + #pragma db id + std::string id; + + obj2_ptr o2; + + #pragma db id_column("obj1_id") value_column("obj3_id") + obj3_ptr_set o3; + + obj4_ptr o4; + + obj5_ptr_set o5; + }; + + #pragma db object + struct obj2 + { + #pragma db id auto + int id; + + // one-to-one + // + #pragma db inverse(o2) + obj1_ptr o1; + + std::string str; + }; + + #pragma db object + struct obj3 + { + std::string str; + + // one(i)-to-many + // + #pragma db inverse (o3) + obj1_ptr o1; + + #pragma db id auto + int id; + }; + + #pragma db object + struct obj4 + { + #pragma db id auto + int id; + + std::string str; + + // many(i)-to-one + // + #pragma db inverse (o4) + obj1_ptr_set o1; + }; + + #pragma db object + struct obj5 + { + #pragma db id auto + int id; + + std::string str; + + // many(i)-to-many + // + #pragma db inverse (o5) + obj1_ptr_set o1; + }; + + inline obj1:: + ~obj1 () + { + delete o2; + for (obj3_ptr_set::iterator i (o3.begin ()); i != o3.end (); ++i) + delete *i; + delete o4; + for (obj5_ptr_set::iterator i (o5.begin ()); i != o5.end (); ++i) + delete *i; + } +} + +// Test shared_ptr/weak_ptr. +// +#pragma db namespace table("t2_") +namespace test2 +{ + using std::shared_ptr; + using std::weak_ptr; + + struct obj1; + struct obj2; + struct obj3; + struct obj4; + struct obj5; + + typedef shared_ptr<obj1> obj1_ptr; + typedef shared_ptr<obj2> obj2_ptr; + typedef shared_ptr<obj3> obj3_ptr; + typedef shared_ptr<obj4> obj4_ptr; + typedef shared_ptr<obj5> obj5_ptr; + + typedef weak_ptr<obj1> obj1_wptr; + + typedef std::vector<obj1_wptr> obj1_wptr_vec; + typedef std::vector<obj3_ptr> obj3_ptr_vec; + typedef std::vector<obj5_ptr> obj5_ptr_vec; + + #pragma db object pointer(obj1_ptr) + struct obj1 + { + #pragma db id + std::string id; + + obj2_ptr o2; + + #pragma db id_column("obj1_id") value_column("obj3_id") + obj3_ptr_vec o3; + + obj4_ptr o4; + obj5_ptr_vec o5; + }; + + #pragma db object pointer(obj2_ptr) + struct obj2 + { + #pragma db id auto + int id; + + std::string str; + + // one(i)-to-one + // + #pragma db inverse(o2) + obj1_wptr o1; + }; + + #pragma db object pointer(obj3_ptr) + struct obj3 + { + #pragma db id auto + int id; + + std::string str; + + // one(i)-to-many + // + #pragma db inverse (o3) + obj1_wptr o1; + }; + + #pragma db object pointer(obj4_ptr) + struct obj4 + { + #pragma db id auto + int id; + + std::string str; + + // many(i)-to-one + // + #pragma db inverse (o4) + obj1_wptr_vec o1; + }; + + #pragma db object pointer(obj5_ptr) + struct obj5 + { + #pragma db id auto + int id; + + std::string str; + + // many(i)-to-many + // + #pragma db inverse (o5) + obj1_wptr_vec o1; + }; +} + +// Test inverse based on points_to. +// +#pragma db namespace table("t3_") +namespace test3 +{ + // Inverse pointer. + // + #pragma db value + struct comp + { + int i; + int j; + }; + + inline bool + operator< (comp x, comp y) {return x.i < y.i || (x.i == y.i && x.j < y.j);} + + struct obj2; + + #pragma db object + struct obj1 + { + #pragma db id + comp id; + + #pragma db inverse(o1) + obj2* o2; + + obj1 (int i = 0, int j = 0): o2 (0) {id.i = i; id.j = j;} + ~obj1 (); + }; + + #pragma db object + struct obj2 + { + #pragma db id auto + int id; + + #pragma db points_to(obj1) + comp o1; + +#ifndef ODB_DATABASE_MYSQL + #pragma db member(o1) on_delete(cascade) +#endif + }; + + inline obj1:: + ~obj1 () {delete o2;} + + // Inverse container of pointers. + // + struct obj4; + + #pragma db object + struct obj3 + { + #pragma db id auto + int id; + + #pragma db inverse(o3) + std::vector<obj4*> o4; + + ~obj3 (); + }; + + #pragma db object + struct obj4 + { + #pragma db id auto + int id; + + #pragma db points_to(obj3) + int o3; + }; + + inline obj3:: + ~obj3 () + { + for (std::vector<obj4*>::iterator i (o4.begin ()); i != o4.end (); ++i) + delete *i; + } +}; + +// Test inverse with nested data members. +// +#pragma db namespace table("t4_") +namespace test4 +{ + // Inverse pointer. + // + struct obj1; + struct obj2; + + #pragma db value + struct obj2_id + { + #pragma db points_to(obj1) + int i; + int j; + }; + + inline bool + operator< (obj2_id x, obj2_id y) + {return x.i < y.i || (x.i == y.i && x.j < y.j);} + + #pragma db object + struct obj1 + { + #pragma db id auto + int id; + + #pragma db inverse(id.i) + obj2* o2; + + obj1 (): o2 (0) {} + ~obj1 (); + }; + + #pragma db object + struct obj2 + { + #pragma db id + obj2_id id; + }; + + inline obj1:: + ~obj1 () {delete o2;} + + // Inverse container of pointers. + // + struct obj3; + struct obj4; + + #pragma db value + struct obj4_id + { + #pragma db points_to(obj3) + int i; + int j; + }; + + inline bool + operator< (obj4_id x, obj4_id y) + {return x.i < y.i || (x.i == y.i && x.j < y.j);} + + #pragma db object + struct obj3 + { + #pragma db id auto + int id; + + #pragma db inverse(id.i) + std::vector<obj4*> o4; + + ~obj3 (); + }; + + #pragma db object + struct obj4 + { + #pragma db id + obj4_id id; + }; + + inline obj3:: + ~obj3 () + { + for (std::vector<obj4*>::iterator i (o4.begin ()); i != o4.end (); ++i) + delete *i; + } +}; +#endif // TEST_HXX diff --git a/odb-tests/common/inverse/testscript b/odb-tests/common/inverse/testscript new file mode 100644 index 0000000..f7e9960 --- /dev/null +++ b/odb-tests/common/inverse/testscript @@ -0,0 +1,53 @@ +# file : common/inverse/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/lazy-ptr/buildfile b/odb-tests/common/lazy-ptr/buildfile new file mode 100644 index 0000000..d495d2f --- /dev/null +++ b/odb-tests/common/lazy-ptr/buildfile @@ -0,0 +1,41 @@ +# file : common/lazy-ptr/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 lazy_ptr_ \ + --generate-schema \ + --generate-session + +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/lazy-ptr/driver.cxx b/odb-tests/common/lazy-ptr/driver.cxx new file mode 100644 index 0000000..9a3b324 --- /dev/null +++ b/odb-tests/common/lazy-ptr/driver.cxx @@ -0,0 +1,360 @@ +// file : common/lazy-ptr/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test lazy object pointers. +// + +#include <memory> // std::unique_ptr +#include <utility> // std::move +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +namespace test2 +{ + cont::cont (unsigned long i) + : id (i) + { + } + + obj_ptr + create (unsigned int id) + { + obj_ptr r (new obj (id)); + return r; + } + + lazy_obj_ptr + create (database& db, unsigned int id) + { + lazy_obj_ptr r (db, id); + return r; + } +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Raw. + // + { + using namespace test1; + + // persist + // + obj* o1 (new obj (1)); + + { + transaction t (db->begin ()); + db->persist (o1); + t.commit (); + } + + unique_ptr<cont> c1 (new cont (1)); + unique_ptr<cont> c2 (new cont (2)); + + lazy_ptr<obj> lo1 (*db, 1); + obj* o2 (new obj (2)); + + obj* o3 (new obj (3)); + obj* o4 (new obj (4)); + + c1->o.push_back (lo1); + c1->o.push_back (o2); + c2->o.push_back (o3); + c2->o.push_back (o4); + + // Test pointer comparison. + // + assert (lazy_ptr<obj> () == lazy_ptr<obj> ()); + assert (lazy_ptr<obj> (o1) != lazy_ptr<obj> ()); + assert (lo1 != lazy_ptr<obj> ()); + assert (lazy_ptr<obj> (o1) == lazy_ptr<obj> (o1)); + assert (lo1 == lazy_ptr<obj> (*db, o1)); + assert (lo1 != lazy_ptr<obj> (*db, o2)); + + delete o1; + + { + transaction t (db->begin ()); + + db->persist (o2); + db->persist (o3); + db->persist (o4); + + db->persist (*c1); + db->persist (*c2); + + t.commit (); + } + + // load + // + { + session s; + transaction t (db->begin ()); + unique_ptr<cont> c (db->load<cont> (1)); + obj* o (db->load<obj> (1)); + + // Not loaded. + // + assert (c->o.size () == 2); + assert (!c->o[0].loaded ()); + assert (!c->o[1].loaded ()); + + assert (!o->c.loaded ()); + + // Correct object ids. + // + assert (c->o[0].object_id () == o->id); + assert (o->c.object_id () == c->id); + + // Load. + // + cont* cl (o->c.load ()); + obj* ol (c->o[0].load ()); + + assert (cl == c.get ()); + assert (ol == o); + + // Test unload/reload. + // + o->c.unload (); + assert (!o->c.loaded ()); + o->c.load (); + assert (o->c.loaded ()); + + t.commit (); + } + } + + // std::unique_ptr + // + { + using namespace test2; + + // persist + // + { + obj_ptr o1 (new obj (1)); + transaction t (db->begin ()); + db->persist (*o1); + t.commit (); + } + + cont_ptr c1 (new cont (1)); + cont_ptr c2 (new cont (2)); + + lazy_obj_ptr lo1 = create (*db, 1); + lo1 = create (*db, 1); + + c1->o = std::move (lo1); + c2->o = create (2); + + { + transaction t (db->begin ()); + + db->persist (*c2->o); + + db->persist (*c1); + db->persist (*c2); + + t.commit (); + } + + // load + // + { + session s; + transaction t (db->begin ()); + cont_ptr c (db->load<cont> (1)); + obj* o (db->load<obj> (1)); + + // Not loaded. + // + assert (!c->o.loaded ()); + assert (!o->c.loaded ()); + + // Correct object ids. + // + assert (c->o.object_id () == o->id); + assert (o->c.object_id () == c->id); + + // Load. + // + cont* cl (o->c.load ()); + const obj_ptr& ol (c->o.load ()); + + assert (cl == c.get ()); + assert (ol.get () == o); + + t.commit (); + } + + // unload/reload + // + { + // No session. + transaction t (db->begin ()); + cont_ptr c (db->load<cont> (1)); + + assert (!c->o.loaded ()); + c->o.load (); + assert (c->o.loaded ()); + c->o.unload (); + assert (!c->o.loaded ()); + c->o.load (); + assert (c->o.loaded ()); + + t.commit (); + } + } + + // Shared pointer from C++11 or TR1. + // + { + using namespace test3; + + // persist + // + shared_ptr<cont> c1 (new cont (1)); + + { + transaction t (db->begin ()); + db->persist (c1); + t.commit (); + } + + lazy_shared_ptr<cont> lc1 (*db, 1); + shared_ptr<cont> c2 (new cont (2)); + + shared_ptr<obj> o1 (new obj (1)); + shared_ptr<obj> o2 (new obj (2)); + + shared_ptr<obj> o3 (new obj (3)); + shared_ptr<obj> o4 (new obj (4)); + + o1->c = lc1; + o2->c = lc1; + o3->c = c2; + o4->c = c2; + + // Test pointer comparison. + // + assert (lazy_shared_ptr<cont> () == lazy_shared_ptr<cont> ()); + assert (lazy_shared_ptr<cont> (c1) != lazy_shared_ptr<cont> ()); + assert (lc1 != lazy_shared_ptr<cont> ()); + assert (lazy_shared_ptr<cont> (c1) == lazy_shared_ptr<cont> (c1)); + assert (lc1 == lazy_shared_ptr<cont> (*db, c1)); + assert (lc1 != lazy_shared_ptr<cont> (*db, c2)); + + // Test move constructors. + // + { + lazy_shared_ptr<cont> tmp (*db, 1); + lazy_shared_ptr<cont> l (std::move (tmp)); + assert (lc1 == l); + } + + { + shared_ptr<cont> tmp (c1); + lazy_shared_ptr<cont> l (*db, std::move (tmp)); + assert (lc1 == l); + } + + { + transaction t (db->begin ()); + + db->persist (o1); + db->persist (o2); + db->persist (o3); + db->persist (o4); + + db->persist (c2); + + t.commit (); + } + + // load + // + { + session s; + transaction t (db->begin ()); + shared_ptr<cont> c (db->load<cont> (1)); + shared_ptr<obj> o (db->load<obj> (1)); + + // Not loaded. + // + assert (c->o.size () == 2); + assert (!c->o[0].loaded ()); + assert (!c->o[1].loaded ()); + + assert (!o->c.loaded ()); + + // Correct object ids. + // + assert (c->o[0].object_id () == o->id); + assert (o->c.object_id () == c->id); + + // Load. + // + shared_ptr<cont> cl (o->c.load ()); + shared_ptr<obj> ol (c->o[0].load ()); + + assert (cl == c); + assert (ol == o); + + t.commit (); + } + + // Test lazy weak locking and reloading. + // + { + // No session. + transaction t (db->begin ()); + shared_ptr<cont> c (db->load<cont> (1)); + + // Lock. + // + assert (!c->o[1].loaded ()); + lazy_shared_ptr<obj> l (c->o[1].lock ()); + assert (!l.loaded ()); + assert (l.object_id () == c->o[1].object_id ()); + + // Reload. + // + assert (!c->o[1].loaded ()); + shared_ptr<obj> ol (c->o[1].load ()); + assert (c->o[1].loaded ()); + ol.reset (); + assert (!c->o[1].loaded ()); + ol = c->o[1].load (); + assert (c->o[1].loaded ()); + + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/lazy-ptr/test.hxx b/odb-tests/common/lazy-ptr/test.hxx new file mode 100644 index 0000000..f946029 --- /dev/null +++ b/odb-tests/common/lazy-ptr/test.hxx @@ -0,0 +1,147 @@ +// file : common/lazy-ptr/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> +#include <string> +#include <memory> + +#include <odb/core.hxx> +#include <odb/lazy-ptr.hxx> + +// Raw pointer. +// +#pragma db namespace table("t1_") +namespace test1 +{ + using odb::lazy_ptr; + class obj; + + #pragma db object + class cont + { + public: + cont () {} + cont (unsigned long i): id (i) {} + ~cont (); + + #pragma db id + unsigned long id; + + typedef std::vector<lazy_ptr<obj> > obj_list; + + #pragma db value_not_null + obj_list o; + }; + + #pragma db object + class obj + { + public: + obj () {} + obj (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + #pragma db inverse(o) not_null + lazy_ptr<cont> c; // weak + }; + + inline cont:: + ~cont () + { + for (obj_list::iterator i (o.begin ()); i != o.end (); ++i) + if (obj* p = i->get ()) + delete p; + } +} + +// std::auto_ptr/std::unique_ptr +// +#pragma db namespace table("t2_") +namespace test2 +{ + using odb::lazy_ptr; + + class obj; + class cont; + + typedef std::unique_ptr<obj> obj_ptr; + typedef std::unique_ptr<cont> cont_ptr; + typedef odb::lazy_unique_ptr<obj> lazy_obj_ptr; + + #pragma db object + class cont + { + public: + cont () = default; + cont (unsigned long id); + + #pragma db id + unsigned long id; + + #pragma db not_null + lazy_obj_ptr o; + }; + + #pragma db object + class obj + { + public: + obj () = default; + obj (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + #pragma db inverse(o) not_null + lazy_ptr<cont> c; // weak + }; +} + +// shared_ptr +// +#pragma db namespace table("t3_") +namespace test3 +{ + using std::shared_ptr; + using odb::lazy_shared_ptr; + using odb::lazy_weak_ptr; + + class obj; + + #pragma db object pointer(shared_ptr<cont>) + class cont + { + public: + cont () {} + cont (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + typedef std::vector<lazy_weak_ptr<obj> > obj_list; + + #pragma db inverse(c) value_not_null + obj_list o; + }; + + #pragma db object pointer(shared_ptr<obj>) + class obj + { + public: + obj () {} + obj (unsigned long i): id (i) {} + + #pragma db id + unsigned long id; + + #pragma db not_null + lazy_shared_ptr<cont> c; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/lazy-ptr/testscript b/odb-tests/common/lazy-ptr/testscript new file mode 100644 index 0000000..023aca6 --- /dev/null +++ b/odb-tests/common/lazy-ptr/testscript @@ -0,0 +1,53 @@ +# file : common/lazy-ptr/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/lifecycle/buildfile b/odb-tests/common/lifecycle/buildfile new file mode 100644 index 0000000..b5b2b00 --- /dev/null +++ b/odb-tests/common/lifecycle/buildfile @@ -0,0 +1,40 @@ +# file : common/lifecycle/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 lifecycle_ \ + --generate-schema + +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/lifecycle/driver.cxx b/odb-tests/common/lifecycle/driver.cxx new file mode 100644 index 0000000..a01d5bd --- /dev/null +++ b/odb-tests/common/lifecycle/driver.cxx @@ -0,0 +1,248 @@ +// file : common/lifecycle/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test object state transistions. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Database operation out of transaction. + // + try + { + object o (1); + db->persist (o); + assert (false); + } + catch (const not_in_transaction&) + { + } + + // Transient. + // + try + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + + // Persistent. + // + { + object o (1); + o.str_ = "value 1"; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + + try + { + transaction t (db->begin ()); + db->persist (o); + assert (false); + t.commit (); + } + catch (const object_already_persistent&) + { + } + } + + // Find. + // + { + transaction t (db->begin ()); + + unique_ptr<object> o1 (db->find<object> (1)); + assert (o1.get () != 0 && o1->str_ == "value 1"); + + unique_ptr<object> o2 (db->find<object> (2)); + assert (o2.get () == 0); + + t.commit (); + } + + // Find (into existing). + // + { + object o; + + transaction t (db->begin ()); + + assert (db->find (1, o)); + assert (o.str_ == "value 1"); + + assert (!db->find (2, o)); + + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + assert (o->str_ == "value 1"); + t.commit (); + + try + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (2)); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + + // Load (into existing). + // + { + object o; + + transaction t (db->begin ()); + db->load (1, o); + assert (o.str_ == "value 1"); + t.commit (); + + try + { + transaction t (db->begin ()); + db->load (2, o); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + + // Reload. + // + { + object o; + + transaction t (db->begin ()); + db->load (1, o); + o.str_ = "junk"; + db->reload (o); + assert (o.str_ == "value 1"); + t.commit (); + + try + { + transaction t (db->begin ()); + o.id_ = 2; + db->reload (o); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + + // Modified. + // + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + o->str_ = "value 2"; + db->update (*o); + t.commit (); + + try + { + transaction t (db->begin ()); + o->id_ = 2; + db->update (*o); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + assert (o->str_ == "value 2"); + t.commit (); + } + + // Update of unmodified object. + // + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + db->update (*o); + t.commit (); + } + + // Transient. + // + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + db->erase (*o); + t.commit (); + + try + { + transaction t (db->begin ()); + db->erase<object> (1); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + + try + { + transaction t (db->begin ()); + unique_ptr<object> o (db->load<object> (1)); + assert (false); + t.commit (); + } + catch (const object_not_persistent&) + { + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/lifecycle/test.hxx b/odb-tests/common/lifecycle/test.hxx new file mode 100644 index 0000000..8d260d2 --- /dev/null +++ b/odb-tests/common/lifecycle/test.hxx @@ -0,0 +1,27 @@ +// file : common/lifecycle/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <odb/core.hxx> + +#pragma db object +struct object +{ + object (unsigned long id) + : id_ (id) + { + } + + object () + { + } + + #pragma db id + unsigned long id_; + std::string str_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/lifecycle/testscript b/odb-tests/common/lifecycle/testscript new file mode 100644 index 0000000..f39297b --- /dev/null +++ b/odb-tests/common/lifecycle/testscript @@ -0,0 +1,53 @@ +# file : common/lifecycle/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/no-id/buildfile b/odb-tests/common/no-id/buildfile new file mode 100644 index 0000000..1a64401 --- /dev/null +++ b/odb-tests/common/no-id/buildfile @@ -0,0 +1,41 @@ +# file : common/no-id/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 no_id_ \ + --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/no-id/driver.cxx b/odb-tests/common/no-id/driver.cxx new file mode 100644 index 0000000..eee69a5 --- /dev/null +++ b/odb-tests/common/no-id/driver.cxx @@ -0,0 +1,102 @@ +// file : common/no-id/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test persistent classes without id. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + object o1 (1, "aaa"); + object o2 (2, "bbb"); + object o3 (3, "ccc"); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o2); // Ok, since there is no id. + db->persist (o3); + t.commit (); + } + + // Compile errors. + // + { + //db->load<object> (1); + //db->find<object> (1); + //db->update (o1); + //db->erase<object> (1); + } + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // Query. + // + { + transaction t (db->begin ()); + + { + result r (db->query<object> ()); + assert (size (r) == 4); + } + + { + result r (db->query<object> (query::str == "aaa")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->num == 1 && i->str == "aaa"); + object o; + i.load (o); + //i.id (); // Compile-time error. + assert (o.num == 1 && o.str == "aaa"); + assert (++i == r.end ()); + } + + { + result r (db->query<object> (query::num < 3)); + assert (size (r) == 3); + } + + t.commit (); + } + + // Erase (query). + // + { + transaction t (db->begin ()); + assert (db->erase_query<object> (query::num == 2) == 2); + assert (db->erase_query<object> () == 2); + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/no-id/test.hxx b/odb-tests/common/no-id/test.hxx new file mode 100644 index 0000000..c5b5c65 --- /dev/null +++ b/odb-tests/common/no-id/test.hxx @@ -0,0 +1,21 @@ +// file : common/no-id/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object no_id +struct object +{ + object () {} + object (unsigned long n, const std::string& s): num (n), str (s) {} + + unsigned long num; + std::string str; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/no-id/testscript b/odb-tests/common/no-id/testscript new file mode 100644 index 0000000..96b8926 --- /dev/null +++ b/odb-tests/common/no-id/testscript @@ -0,0 +1,53 @@ +# file : common/no-id/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/object/buildfile b/odb-tests/common/object/buildfile new file mode 100644 index 0000000..cb56aee --- /dev/null +++ b/odb-tests/common/object/buildfile @@ -0,0 +1,41 @@ +# file : common/object/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 object_ \ + --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/object/driver.cxx b/odb-tests/common/object/driver.cxx new file mode 100644 index 0000000..1c29417 --- /dev/null +++ b/odb-tests/common/object/driver.cxx @@ -0,0 +1,84 @@ +// file : common/object/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test persistent classes. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test persistent class template instantiation. + // + { + using namespace test1; + + pair_object po; + po.second = "abc"; + + derived d; + d.x = "abc"; + d.n = 123; + + // persist + // + { + transaction t (db->begin ()); + db->persist<pair_object> (po); + db->persist (d); + t.commit (); + } + + // load & check + // + { + transaction t (db->begin ()); + unique_ptr<pair_object> po1 (db->load<pair_object> (po.first)); + unique_ptr<derived> d1 (db->load<derived> (d.id)); + t.commit (); + + assert (po == *po1); + + assert (d.x == d1->x); + assert (d.n == d1->n); + } + + // Test the API confusion. + // + { + transaction t (db->begin ()); + db->update<pair_object> (po); + db->reload<pair_object> (po); + db->erase<pair_object> (po); + + db->query<pair_object> (); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/object/test.hxx b/odb-tests/common/object/test.hxx new file mode 100644 index 0000000..87bac91 --- /dev/null +++ b/odb-tests/common/object/test.hxx @@ -0,0 +1,49 @@ +// file : common/object/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <utility> // std::pair + +#include <odb/core.hxx> + +// Test persistent class template instantiation. +// +#pragma db namespace table("t1_") +namespace test1 +{ + typedef std::pair<unsigned long, std::string> pair_object; + #pragma db object(pair_object) + #pragma db member(pair_object::first) id auto + + #pragma db object abstract + struct base_data + { + #pragma db id auto + unsigned long id; + }; + + template <typename T> + struct base: base_data + { + T x; + }; + + typedef base<std::string> base_derived; + #pragma db object(base_derived) abstract + + #pragma db object + struct derived: base_derived + { + int n; + }; + + // Test instantiation in order to "see" id, etc. + // + typedef base<int> int_base; + #pragma db object(int_base) +} + +#endif // TEST_HXX diff --git a/odb-tests/common/object/testscript b/odb-tests/common/object/testscript new file mode 100644 index 0000000..45737c1 --- /dev/null +++ b/odb-tests/common/object/testscript @@ -0,0 +1,53 @@ +# file : common/object/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/optimistic/buildfile b/odb-tests/common/optimistic/buildfile new file mode 100644 index 0000000..06af705 --- /dev/null +++ b/odb-tests/common/optimistic/buildfile @@ -0,0 +1,41 @@ +# file : common/optimistic/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_optimistic_ \ + --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/optimistic/driver.cxx b/odb-tests/common/optimistic/driver.cxx new file mode 100644 index 0000000..6dfec6e --- /dev/null +++ b/odb-tests/common/optimistic/driver.cxx @@ -0,0 +1,300 @@ +// file : common/optimistic/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test optimistic concurrency support. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +unsigned long +version (const unique_ptr<database>& db, unsigned long id) +{ + typedef odb::query<object_version> query; + typedef odb::result<object_version> result; + + result r (db->query<object_version> (query::id == id)); + return r.empty () ? 0 : r.begin ()->ver; +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + object o (1); + o.num = 123; + o.str = "abc"; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + // Verify initial version in the instance and database. + // + assert (o.ver == 1); + { + transaction t (db->begin ()); + assert (version (db, 1) == 1); + t.commit (); + } + + object c (o); + o.num++; + o.str += 'd'; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + // Verify updated version in the instance and database. + // + assert (o.ver == 2); + { + transaction t (db->begin ()); + assert (version (db, 1) == 2); + t.commit (); + } + + // Verify the data has been updated. + // + { + transaction t (db->begin ()); + unique_ptr<object> o1 (db->load<object> (1)); + t.commit (); + + assert (o1->ver == 2 && o1->num == 124 && o1->str == "abcd"); + } + + // Try to update using outdated object. + // + c.num--; + c.str += 'z'; + + { + transaction t (db->begin ()); + + try + { + db->update (c); + assert (false); + } + catch (const object_changed&) {} + + // Verify the data hasn't changed. + // + unique_ptr<object> o1 (db->load<object> (1)); + assert (o1->ver == 2 && o1->num == 124 && o1->str == "abcd"); + + // Reload the object. + // + db->reload (c); + assert (c.ver == 2 && c.num == 124); + + // Check that we don't reload an object that is up-to-date. + // + c.num--; + db->reload (c); + assert (c.ver == 2 && c.num == 123); + + t.commit (); + } + + // Try to delete using an outdated object. + // + { + transaction t (db->begin ()); + + try + { + db->update (o); + db->erase (c); + assert (false); + } + catch (const object_changed&) {} + + t.commit (); + } + + // Try to delete using an up-to-date object. + // + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + + // Try to update deleted object. + // + { + transaction t (db->begin ()); + + try + { + db->update (o); + assert (false); + } + catch (const object_not_persistent&) + { + assert (false); + } + catch (const object_changed&) {} + + t.commit (); + } + + // Optimistic delete of objects with container requires + // extra logic. Test it here. + // + { + container o ("abc"); + o.nums.push_back (1); + o.nums.push_back (2); + o.nums.push_back (3); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + container c (o); + o.nums.pop_back (); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + // Try to delete using an outdated object. + // + { + transaction t (db->begin ()); + + try + { + db->erase (c); + assert (false); + } + catch (const object_changed&) {} + + // Verify the container data hasn't changed. + // + unique_ptr<container> o1 (db->load<container> ("abc")); + assert (o1->nums.size () == 2 && o1->nums[0] == 1 && o1->nums[1] == 2); + + t.commit (); + } + + // Try to delete using an up-to-date object. + // + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test optimistic class inheritance. This is a shortened version + // of the object test. + // + { + derived o; + o.num = 123; + o.str = "abc"; + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + derived c (o); + o.num++; + o.str += 'd'; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + // Try to update using outdated object. + // + c.num--; + c.str += 'z'; + + { + transaction t (db->begin ()); + + try + { + db->update (c); + assert (false); + } + catch (const object_changed&) {} + + // Reload the object. + // + db->reload (c); + assert (c.ver == 2 && c.num == 124); + + t.commit (); + } + + // Try to delete using an outdated object. + // + { + transaction t (db->begin ()); + + try + { + db->update (o); + db->erase (c); + assert (false); + } + catch (const object_changed&) {} + + t.commit (); + } + + // Try to delete using an up-to-date object. + // + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/optimistic/test.hxx b/odb-tests/common/optimistic/test.hxx new file mode 100644 index 0000000..fcefa3d --- /dev/null +++ b/odb-tests/common/optimistic/test.hxx @@ -0,0 +1,76 @@ +// file : common/optimistic/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +#pragma db object optimistic +struct object +{ + object (): ver (123) {} + object (unsigned long id): id_ (id), ver (123) {} + + #pragma db id + unsigned long id_; + + #pragma db version + unsigned long ver; + + unsigned int num; + std::string str; +}; + +#pragma db view object(object) +struct object_version +{ + unsigned long ver; +}; + +// Optimistic class with a container. +// +#pragma db object optimistic +struct container +{ + container (): ver (123) {} + container (const std::string& id): id_ (id), ver (123) {} + + #pragma db id + std::string id_; + + #pragma db version + unsigned long ver; + + std::vector<unsigned int> nums; +}; + +// Optimistic class inheritance. +// +#pragma db object abstract optimistic +struct base +{ + base (): ver (123) {} + + #pragma db id auto + unsigned long id_; + + #pragma db version + const unsigned long ver; + + std::string str; + + #pragma db readonly + std::string ro; +}; + +#pragma db object +struct derived: base +{ + unsigned int num; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/optimistic/testscript b/odb-tests/common/optimistic/testscript new file mode 100644 index 0000000..b9117c0 --- /dev/null +++ b/odb-tests/common/optimistic/testscript @@ -0,0 +1,53 @@ +# file : common/optimistic/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/pragma/buildfile b/odb-tests/common/pragma/buildfile new file mode 100644 index 0000000..a2eeaa0 --- /dev/null +++ b/odb-tests/common/pragma/buildfile @@ -0,0 +1,39 @@ +# file : common/pragma/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 pragma_ + +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/pragma/driver.cxx b/odb-tests/common/pragma/driver.cxx new file mode 100644 index 0000000..a9cc6e0 --- /dev/null +++ b/odb-tests/common/pragma/driver.cxx @@ -0,0 +1,27 @@ +// file : common/pragma/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test #pragma db parsing. +// + +#include <memory> +#include <iostream> + +#include <odb/exceptions.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main () +{ +} diff --git a/odb-tests/common/pragma/test.hxx b/odb-tests/common/pragma/test.hxx new file mode 100644 index 0000000..6877e73 --- /dev/null +++ b/odb-tests/common/pragma/test.hxx @@ -0,0 +1,40 @@ +// file : common/template/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <odb/core.hxx> + +#pragma db value(bool) type ("INTEGER") + +struct x {int i;}; +#pragma db value (x) + +namespace N +{ + #pragma db object + struct object1 + { + object1 () {} + + #pragma db id + unsigned long id_; + + #pragma db member type ("INTEGER") + bool b_; + }; + + struct object2 + { + object2 () {} + + unsigned long id_; + }; + + #pragma db object (object2) +} + +PRAGMA_DB (member (N::object2::id_) id auto); + +#endif // TEST_HXX diff --git a/odb-tests/common/pragma/testscript b/odb-tests/common/pragma/testscript new file mode 100644 index 0000000..c2697f5 --- /dev/null +++ b/odb-tests/common/pragma/testscript @@ -0,0 +1,49 @@ +# file : common/include/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* &!odb-test.db +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $* +} diff --git a/odb-tests/common/prepared/buildfile b/odb-tests/common/prepared/buildfile new file mode 100644 index 0000000..56a0074 --- /dev/null +++ b/odb-tests/common/prepared/buildfile @@ -0,0 +1,43 @@ +# file : common/prepared/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_prepared_ \ + --generate-schema \ + --generate-query \ + --generate-prepared \ + --omit-unprepared + +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/prepared/driver.cxx b/odb-tests/common/prepared/driver.cxx new file mode 100644 index 0000000..44df651 --- /dev/null +++ b/odb-tests/common/prepared/driver.cxx @@ -0,0 +1,444 @@ +// file : common/prepared/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test prepared query functionality. +// + +#include <memory> // std::unique_ptr +#include <utility> // std::move +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct params +{ + unsigned short age; + std::string name; +}; + +static void +query_factory (const char* name, connection& c) +{ + typedef odb::query<person> query; + + unique_ptr<params> p (new params); + prepared_query<person> pq ( + c.prepare_query<person> ( + name, + query::age > query::_ref (p->age) && + query::name != query::_ref (p->name))); + c.cache_query (pq, move (p)); +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + { + person p1 ("John First", 91); + person p2 ("John Second", 81); + person p3 ("John Third", 71); + person p4 ("John Fourth", 61); + person p5 ("John Fifth", 51); + + transaction t (db->begin ()); + db->persist (p1); + db->persist (p2); + db->persist (p3); + db->persist (p4); + db->persist (p5); + t.commit (); + } + + typedef odb::query<person> query; + typedef odb::prepared_query<person> prep_query; + typedef odb::result<person> result; + + // Uncached query in the same transaction. + // + { + transaction t (db->begin ()); + + unsigned short age (90); + prep_query pq ( + db->prepare_query<person> ( + "person-age-query", + query::age > query::_ref (age))); + + for (unsigned short i (1); i < 6; ++i, age -= 10) + { + result r (pq.execute ()); + assert (size (r) == i); + } + + age = 90; + result r (pq.execute ()); + result::iterator i (r.begin ()); + assert (i != r.end () && i->name_ == "John First" && i->age_ == 91); + assert (++i == r.end ()); + + t.commit (); + } + + // Uncached query in multiple transaction. + // + { + connection_ptr c (db->connection ()); + + unsigned short age (90); + prep_query pq ( + c->prepare_query<person> ( + "person-age-query", + query::age > query::_ref (age))); + + for (unsigned short i (1); i < 6; ++i, age -= 10) + { + transaction t (c->begin ()); + + result r (pq.execute ()); + assert (size (r) == i); + + t.commit (); + } + + transaction t (c->begin ()); + + age = 90; + result r (pq.execute ()); + result::iterator i (r.begin ()); + assert (i != r.end () && i->name_ == "John First" && i->age_ == 91); + assert (++i == r.end ()); + + t.commit (); + } + + // Cached query without parameters. + // + { + for (unsigned short i (1); i < 6; ++i) + { + transaction t (db->begin ()); + + prep_query pq (db->lookup_query<person> ("person-val-age-query")); + + if (!pq) + { + assert (i == 1); + pq = db->prepare_query<person> ( + "person-val-age-query", + query::age > 90); + db->cache_query (pq); + } + else if (i == 2) + { + try + { + db->cache_query (pq); + assert (false); + } + catch (const odb::prepared_already_cached&) + { + } + } + + result r (pq.execute ()); + assert (size (r) == 1); + + t.commit (); + } + } + + // Cached query with parameters. + // + { + for (unsigned short i (1); i < 6; ++i) + { + transaction t (db->begin ()); + + unsigned short* age; + prep_query pq (db->lookup_query<person> ("person-ref-age-query", age)); + + if (!pq) + { + assert (i == 1); + + unique_ptr<unsigned short> p (new unsigned short); + age = p.get (); + pq = db->prepare_query<person> ( + "person-ref-age-query", + query::age > query::_ref (*age)); + + db->cache_query (pq, move (p)); + } + else if (i == 2) + { + // Object type mismatch. + // + try + { + db->lookup_query<int> ("person-ref-age-query", age); + assert (false); + } + catch (const odb::prepared_type_mismatch&) + { + } + + // Parameters type mismatch. + // + try + { + int* age; + db->lookup_query<person> ("person-ref-age-query", age); + assert (false); + } + catch (const odb::prepared_type_mismatch&) + { + } + } + + *age = 100 - i * 10; + result r (pq.execute ()); + assert (size (r) == i); + + t.commit (); + } + } + + // Cached query with factory. + // + { + db->query_factory ("person-params-query", &query_factory); + + for (unsigned int i (1); i < 6; ++i) + { + transaction t (db->begin ()); + + params* p; + prep_query pq (db->lookup_query<person> ("person-params-query", p)); + assert (pq); + + p->age = 100 - i * 10; + p->name = "John First"; + result r (pq.execute ()); + assert (size (r) == i - 1); + + t.commit (); + } + + db->query_factory ("person-params-query", + database::query_factory_ptr ()); + } + + // Cached query with wildcard factory. + // + { + db->query_factory ("", &query_factory); + + for (unsigned int i (1); i < 6; ++i) + { + transaction t (db->begin ()); + + params* p; + prep_query pq (db->lookup_query<person> ("person-params-query-1", p)); + assert (pq); + + p->age = 100 - i * 10; + p->name = "John First"; + result r (pq.execute ()); + assert (size (r) == i - 1); + + t.commit (); + } + + db->query_factory ("", database::query_factory_ptr ()); + } + + // Cached query with lambda factory. + // + { + db->query_factory ( + "person-params-query-2", + [] (const char* name, connection& c) + { + typedef odb::query<person> query; + + unique_ptr<params> p (new params); + prepared_query<person> pq ( + c.prepare_query<person> ( + name, + query::age > query::_ref (p->age) && + query::name != query::_ref (p->name))); + c.cache_query (pq, move (p)); + }); + + for (unsigned int i (1); i < 6; ++i) + { + transaction t (db->begin ()); + + params* p; + prep_query pq (db->lookup_query<person> ("person-params-query-2", p)); + assert (pq); + + p->age = 100 - i * 10; + p->name = "John First"; + result r (pq.execute ()); + assert (size (r) == i - 1); + + t.commit (); + } + + db->query_factory ("person-params-query-2", + database::query_factory_ptr ()); + } + + // Cached query with lambda factory using closure. Forces nonoptimized + // representation of std::function. + // + { + const std::string person_name ("John First"); + + db->query_factory ( + "person-params-query-3", + [person_name] (const char* name, connection& c) + { + typedef odb::query<person> query; + + prepared_query<person> pq ( + c.prepare_query<person> ( + name, + query::age > 50 && query::name != person_name)); + c.cache_query (pq); + }); + + { + transaction t (db->begin ()); + + prep_query pq (db->lookup_query<person> ("person-params-query-3")); + assert (pq); + + result r (pq.execute ()); + assert (size (r) == 4); + + t.commit (); + } + + db->query_factory ("person-params-query-3", nullptr); + } + + // View prepared query. + // + { + typedef odb::query<person_view> query; + typedef odb::prepared_query<person_view> prep_query; + typedef odb::result<person_view> result; + + transaction t (db->begin ()); + + unsigned short age (90); + prep_query pq ( + db->prepare_query<person_view> ( + "person-view-age-query", + query::age > query::_ref (age))); + + for (unsigned short i (1); i < 6; ++i, age -= 10) + { + result r (pq.execute ()); + assert (size (r) == i); + } + + age = 90; + result r (pq.execute ()); + result::iterator i (r.begin ()); + assert (i != r.end () && i->name == "John First" && i->age == 91); + assert (++i == r.end ()); + + t.commit (); + } + + // By-ref parameter image growth. + // + { + transaction t (db->begin ()); + + string name; + prep_query pq ( + db->prepare_query<person> ( + "person-name-query", + query::name != query::_ref (name))); + + { + name = "John First"; + result r (pq.execute ()); + assert (size (r) == 4); + } + + { + name.assign (2048, 'x'); + result r (pq.execute ()); + assert (size (r) == 5); + } + + t.commit (); + } + + // Test execute_one() and execute_value(). + // + { + transaction t (db->begin ()); + + person p ("John Doe", 23); + db->persist (p); + + prep_query pq1 ( + db->prepare_query<person> ("query-1", query::id == p.id_)); + prep_query pq0 ( + db->prepare_query<person> ("query-0", query::id == p.id_ + 1)); + + { + unique_ptr<person> p (pq1.execute_one ()); + assert (p.get () != 0 && p->name_ == "John Doe"); + } + + { + unique_ptr<person> p (pq0.execute_one ()); + assert (p.get () == 0); + } + + { + person p; + assert (pq1.execute_one (p) && p.name_ == "John Doe"); + } + + { + person p ("", 0); + assert (!pq0.execute_one (p) && + p.id_ == 0 && p.name_.empty () && p.age_ == 0); + } + + { + person p (pq1.execute_value ()); + assert (p.name_ == "John Doe"); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/prepared/test.hxx b/odb-tests/common/prepared/test.hxx new file mode 100644 index 0000000..db16e67 --- /dev/null +++ b/odb-tests/common/prepared/test.hxx @@ -0,0 +1,32 @@ +// file : common/prepared/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object +struct person +{ + person (): id_ (0) {} + person (const std::string& name, unsigned short age) + : id_ (0), name_ (name), age_ (age) {} + + #pragma db id auto + unsigned long id_; + + std::string name_; + unsigned short age_; +}; + +#pragma db view object(person) +struct person_view +{ + std::string name; + unsigned short age; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/prepared/testscript b/odb-tests/common/prepared/testscript new file mode 100644 index 0000000..d378a79 --- /dev/null +++ b/odb-tests/common/prepared/testscript @@ -0,0 +1,53 @@ +# file : common/prepared/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/query/array/buildfile b/odb-tests/common/query/array/buildfile new file mode 100644 index 0000000..fcaffd4 --- /dev/null +++ b/odb-tests/common/query/array/buildfile @@ -0,0 +1,45 @@ +# file : common/query/array/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_query_array_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +if $oracle + odb_options += --sql-name-case oracle:upper + +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/query/array/driver.cxx b/odb-tests/common/query/array/driver.cxx new file mode 100644 index 0000000..43d91f2 --- /dev/null +++ b/odb-tests/common/query/array/driver.cxx @@ -0,0 +1,200 @@ +// file : common/query/array/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test query support for C arrays. +// + +#include <string> +#include <memory> // std::unique_ptr +#include <cstring> // std::memcpy +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.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; + +#ifndef MULTI_DATABASE +# if defined(DATABASE_MYSQL) +const odb::mysql::database_type_id bt = odb::mysql::id_blob; +# elif defined(DATABASE_SQLITE) +const odb::sqlite::database_type_id bt = odb::sqlite::id_blob; +# elif defined(DATABASE_PGSQL) +const odb::pgsql::database_type_id bt = odb::pgsql::id_bytea; +# elif defined(DATABASE_ORACLE) +const odb::oracle::database_type_id bt = odb::oracle::id_raw; +# elif defined(DATABASE_MSSQL) +const odb::mssql::database_type_id bt = odb::mssql::id_binary; +# else +# error unknown database +# endif +#endif + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + typedef odb::query<object> query; + + const char buf[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6}; + + // + // + { + object o1 (1, "abc", buf); + object o2 (2, "bcd", buf); + object o3 (3, "cde", buf); + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + db->persist (o3); + t.commit (); + } + + { + transaction t (db->begin ()); + + // string + // +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::s == "abc")) == 1); + assert (size (db->query<object> (query::s == query::_val ("bcd"))) == 1); + assert (size (db->query<object> ("s = " + query::_val ("bcd"))) == 1); + assert (size (db->query<object> ("s = " + query::_ref ("bcd"))) == 1); +#endif + + { + char a[] = "bcd"; + char* ra = a; +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::s == a)) == 1); + assert (size (db->query<object> (query::s == query::_val (a))) == 1); +#endif + assert (size (db->query<object> (query::s == query::_ref (ra))) == 1); +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("s = " + query::_val (a))) == 1); + assert (size (db->query<object> ("s = " + query::_ref (a))) == 1); +#endif + } + + { + const char a[] = "bcd"; + const char* ra = a; +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::s == a)) == 1); + assert (size (db->query<object> (query::s == query::_val (a))) == 1); +#endif + assert (size (db->query<object> (query::s == query::_ref (ra))) == 1); +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("s = " + query::_val (a))) == 1); + assert (size (db->query<object> ("s = " + query::_ref (a))) == 1); +#endif + } + + { + const char* p = "cde"; +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::s == p)) == 1); + assert (size (db->query<object> (query::s == query::_val (p))) == 1); +#endif + assert (size (db->query<object> (query::s == query::_ref (p))) == 1); +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("s = " + query::_val (p))) == 1); + assert (size (db->query<object> ("s = " + query::_ref (p))) == 1); +#endif + } + + { + char a[] = "cde"; + char* p = a; +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::s == p)) == 1); + assert (size (db->query<object> (query::s == query::_val (p))) == 1); +#endif + assert (size (db->query<object> (query::s == query::_ref (p))) == 1); +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("s = " + query::_val (p))) == 1); + assert (size (db->query<object> ("s = " + query::_ref (p))) == 1); +#endif + } + +#ifndef MULTI_DATABASE + string s ("abc"); + //assert (size (db->query<object> (query::s == s)) == 1); + assert (size (db->query<object> (query::s == s.c_str ())) == 1); + //assert (size (db->query<object> (query::s == query::_val (s))) == 1); + assert (size (db->query<object> (query::s == query::_val (s.c_str ()))) == 1); + + assert (size (db->query<object> ("s = " + query::_val (s))) == 1); + assert (size (db->query<object> ("s = " + query::_ref (s))) == 1); +#endif + + assert (size (db->query<object> (query::s == query::s1)) == 3); + + // std::array + // + array<char, 17> a; + memcpy (a.data (), "abc", 4); // VC++ strcpy deprecation. + +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::a == a)) == 1); + assert (size (db->query<object> (query::a == query::_val (a))) == 1); +#endif + assert (size (db->query<object> (query::a == query::_ref (a))) == 1); +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("a = " + query::_val (a))) == 1); + assert (size (db->query<object> ("a = " + query::_ref (a))) == 1); +#endif + + // char + // + assert (size (db->query<object> (query::c == 'a')) == 1); + + char c ('b'); + assert (size (db->query<object> (query::c == query::_val (c))) == 1); + assert (size (db->query<object> (query::c == query::_ref (c))) == 1); + +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("c = " + query::_val ('c'))) == 1); + assert (size (db->query<object> ("c = " + query::_ref (c))) == 1); +#endif + + assert (size (db->query<object> (query::c == query::c1)) == 3); + + // buffer + // +#ifndef MULTI_DATABASE + assert (size (db->query<object> (query::b == buf)) == 3); + assert (size (db->query<object> (query::b == query::_val (buf))) == 3); +#endif + + assert (size (db->query<object> (query::b == query::_ref (buf))) == 3); + +#ifndef MULTI_DATABASE + assert (size (db->query<object> ("b = " + query::_val<bt> (buf))) == 3); + assert (size (db->query<object> ("b = " + query::_ref<bt> (buf))) == 3); +#endif + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/query/array/test.hxx b/odb-tests/common/query/array/test.hxx new file mode 100644 index 0000000..f0d5f3b --- /dev/null +++ b/odb-tests/common/query/array/test.hxx @@ -0,0 +1,70 @@ +// file : common/query/array/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <array> +#include <cstring> // std::memcpy, std::strlen + +#include <odb/core.hxx> + +#pragma db object +struct object +{ + object () {} + object (unsigned long id, const char* s, const char* b) + : id_ (id) + { + std::memcpy (s_, s, std::strlen (s) + 1); // VC++ strncpy deprecation. + std::memcpy (s1_, s, std::strlen (s) + 1); + std::memcpy (a_.data (), s, std::strlen (s) + 1); + c_ = c1_ = *s; + std::memcpy (b_, b, sizeof (b_)); + } + + #pragma db id + unsigned long id_; + + char s_[17]; + char s1_[17]; + +#ifdef ODB_COMPILER +# if defined(ODB_DATABASE_MYSQL) || \ + defined(ODB_DATABASE_PGSQL) || \ + defined(ODB_DATABASE_ORACLE) || \ + defined(ODB_DATABASE_MSSQL) +# pragma db type("VARCHAR(16)") +# elif defined(ODB_DATABASE_SQLITE) +# pragma db type("TEXT") +# elif defined(ODB_DATABASE_COMMON) +# pragma db type("DYMMU") // Necessary to make it a value. +# else +# error unknown database +# endif +#endif + std::array<char, 17> a_; + + char c_; + char c1_; + +#ifdef ODB_COMPILER +# if defined(ODB_DATABASE_MYSQL) +# pragma db type("BINARY(16)") +# elif defined(ODB_DATABASE_SQLITE) +# pragma db type("BLOB") +# elif defined(ODB_DATABASE_PGSQL) +# pragma db type("BYTEA") +# elif defined(ODB_DATABASE_ORACLE) +# pragma db type("RAW(16)") +# elif defined(ODB_DATABASE_MSSQL) +# pragma db type("BINARY(16)") +# elif defined(ODB_DATABASE_COMMON) +# else +# error unknown database +# endif +#endif + char b_[16]; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/query/array/testscript b/odb-tests/common/query/array/testscript new file mode 100644 index 0000000..37f7158 --- /dev/null +++ b/odb-tests/common/query/array/testscript @@ -0,0 +1,53 @@ +# file : common/query/array/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/query/basics/buildfile b/odb-tests/common/query/basics/buildfile new file mode 100644 index 0000000..e38e6fe --- /dev/null +++ b/odb-tests/common/query/basics/buildfile @@ -0,0 +1,42 @@ +# file : common/query/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_query_basics_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +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/query/basics/driver.cxx b/odb-tests/common/query/basics/driver.cxx new file mode 100644 index 0000000..eb7e2a7 --- /dev/null +++ b/odb-tests/common/query/basics/driver.cxx @@ -0,0 +1,668 @@ +// file : common/query/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test basic query support. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +void +print (result<person>& r) +{ + for (result<person>::iterator i (r.begin ()); i != r.end (); ++i) + { + unique_ptr<person> o (i.load ()); + cout << *o << endl; + } + cout << endl; +} + +const char* names[] = { "John", "Jane", "Joe" }; +const char** names_end = names + sizeof (names)/sizeof (names[0]); + +const char* key_data[] = { "\x23\x03\x15", "\x13\x13\x54", "\x08\x62\x35" }; + +int +main (int argc, char* argv[]) +{ + buffer + key1 (key_data[0], key_data[0] + 3), + key2 (key_data[1], key_data[1] + 3), + key3 (key_data[2], key_data[2] + 3); + + try + { + unique_ptr<database> db (create_database (argc, argv)); + odb::database_id db_id (db->id ()); + + typedef odb::query<person> query; + typedef odb::result<person> result; + + // + // + { + person p1 (1, "John", "Doe", 30, true, key1); + person p2 (2, "Jane", "Doe", 29, true, key2); + person p3 (3, "Joe", "Dirt", 31, false, key3); + p3.middle_name_.reset (new string ("Squeaky")); + person p4 (4, "Johansen", "Johansen", 32, false); + p4.middle_name_.reset (new string ("J")); + + transaction t (db->begin ()); + db->persist (p1); + db->persist (p2); + db->persist (p3); + db->persist (p4); + t.commit (); + } + + // + // Native queries. + // + + // Compilation tests. + // +#ifndef MULTI_DATABASE + if (false) + { + string name; + unsigned short age; + + db->query<person> ("age = " + query::_ref (age)); + db->query<person> ("age = " + query::_val (age)); + + query age_q (query::_ref (age) + " = age"); + query name_q ("first = " + query::_val (name)); + query q (age_q + "AND" + name_q); + + db->query<person> (q); + db->query<person> (age_q + "OR" + + name_q + "OR" + + "age < " + query::_ref (age)); + + query q1 (query::_val (name)); + q1 += " = first"; + } +#endif + + // Select-all query. + // + cout << "test 001" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> ()); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + person p; + i.load (p); + cout << p << endl; + } + + t.commit (); + } + + // Select-all query with order by. + // + cout << "test 002" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> ("ORDER BY" + query::age)); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + person& p (*i); + + cout << p.first_name_; + + if (i->middle_name_.get () != 0) + cout << ' ' << *i->middle_name_; + + cout << ' ' << i->last_name_ << ' ' << i->age_ << + (i->married_ ? " married" : " single") << endl; + } + cout << endl; + + t.commit (); + } + + // String query. + // + cout << "test 003" << endl; + { + transaction t (db->begin ()); + + result r; + if (db_id != odb::id_oracle) + r = db->query<person> ("age >= 30 AND last = 'Doe'"); + else + r = db->query<person> ("\"age\" >= 30 AND \"last\" = 'Doe'"); + + print (r); + t.commit (); + } + + // Value binding. + // + cout << "test 004" << endl; + { + transaction t (db->begin ()); + + const char* name = "Doe"; + +#if defined(MULTI_DATABASE) + result r ( + db->query<person> ( + query::age >= query::_val (30) && + query::last_name == query::_val (name))); + +#elif defined(DATABASE_ORACLE) + result r ( + db->query<person> ( + "\"age\" >= " + query::_val (30) + "AND" + + "\"last\" = " + query::_val (name))); +#else + result r ( + db->query<person> ( + "age >= " + query::_val (30) + "AND" + + "last = " + query::_val (name))); +#endif + + print (r); + t.commit (); + } + + // Reference binding. + // + cout << "test 005" << endl; + { + transaction t (db->begin ()); + + string name; + unsigned short age (0); + +#if defined(MULTI_DATABASE) + query q (query::age >= query::_ref (age) && + query::last_name == query::_ref (name)); +#elif defined(DATABASE_ORACLE) + query q ("\"age\" >= " + query::_ref (age) + "AND" + + "\"last\" = " + query::_ref (name)); +#else + query q ("age >= " + query::_ref (age) + "AND" + + "last = " + query::_ref (name)); +#endif + + name = "Doe"; + age = 30; + result r (db->query<person> (q)); + print (r); + + name = "Dirt"; + age = 31; + r = db->query<person> (q); + print (r); + + t.commit (); + } + + // + // Language-embedded queries. + // + + // Compilation tests. + // + if (false) + { + string name; + unsigned short age; + + // Column operators. + // + query q1 (query::married); + db->query<person> (query::married); + db->query<person> (query::married == true); + + //db->query<person> (query::age); + + db->query<person> (query::age == 30); + db->query<person> (query::age == age); + db->query<person> (query::age == query::_val (30)); + db->query<person> (query::age == query::_val (age)); + db->query<person> (query::age == query::_ref (age)); + //db->query<person> (query::age == "123"); + //db->query<person> ("123" == query::age); + //db->query<person> (query::age == query::_val ("123")); + //db->query<person> (query::age == query::_ref (name)); + db->query<person> (query::last_name == "Doe"); + db->query<person> (query::last_name == name); +#ifndef MULTI_DATABASE + db->query<person> (query::last_name == query::_val ("Doe")); +#endif + db->query<person> (query::last_name == query::_val (name)); + db->query<person> (query::last_name == query::_ref (name)); + //db->query<person> (query::last_name == 30); + //db->query<person> (query::last_name == query::_val (30)); + //db->query<person> (query::last_name == query::_ref (age)); + + db->query<person> (query::last_name.is_null ()); + db->query<person> (query::last_name.is_not_null ()); + + db->query<person> (query::first_name == query::last_name); + + db->query<person> (query::first_name.in ("John", "Jane")); + db->query<person> (query::first_name.in_range (names, names_end)); + + db->query<person> (query::first_name.like ("J%")); + db->query<person> (query::first_name.like ("J%!%", "!")); + + // Query operators. + // + db->query<person> (query::age == 30 && query::last_name == "Doe"); + db->query<person> (query::age == 30 || query::last_name == "Doe"); + db->query<person> (!(query::age == 30 || query::last_name == "Doe")); + db->query<person> ((query::last_name == "Doe") + "ORDER BY age"); + } + + // Test is_null/is_not_null. + // + cout << "test 006" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::middle_name.is_null ())); + print (r); + r = db->query<person> (query::middle_name.is_not_null ()); + print (r); + t.commit (); + } + + // Test boolean columns. + // + cout << "test 007" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::married)); + print (r); + r = db->query<person> (!query::married); + print (r); + t.commit (); + } + + // Test implicit by-value, explicit by-value, and by-reference. + // + cout << "test 008" << endl; + { + string name ("Dirt"); + + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "Doe")); + print (r); + r = db->query<person> (query::last_name == query::_val (name)); + print (r); + query q (query::last_name == query::_ref (name)); + name = "Doe"; + r = db->query<person> (q); + print (r); + t.commit (); + } + + // Test column operators (==, !=, <, >, <=, >=). + // + cout << "test 009" << endl; + { + transaction t (db->begin ()); + + // == + // + result r (db->query<person> (query::last_name == "Doe")); + print (r); + + // != + // + r = db->query<person> (query::last_name != "Doe"); + print (r); + + // < + // + r = db->query<person> (query::age < 31); + print (r); + + // > + // + r = db->query<person> (query::age > 30); + print (r); + + // <= + // + r = db->query<person> (query::age <= 30); + print (r); + + // >= + // + r = db->query<person> (query::age >= 31); + print (r); + + t.commit (); + } + + // Test query operators (&&, ||, (), !, +). + // + cout << "test 010" << endl; + { + transaction t (db->begin ()); + + // && + // + result r (db->query<person> ( + query::last_name == "Doe" && query::age == 29)); + print (r); + + // || + // + r = db->query<person> (query::last_name == "Doe" || query::age == 31); + print (r); + + // () + // + r = db->query<person> ( + (query::last_name != "Doe" || query::age == 29) && query::married); + print (r); + + // != + // + r = db->query<person> (!(query::last_name == "Doe")); + print (r); + + // + + // + r = db->query<person> ((query::last_name == "Doe") + + "ORDER BY" + query::age); + print (r); + + t.commit (); + } + + // Test in/in_range. + // + cout << "test 011" << endl; + { + transaction t (db->begin ()); + + result r (db->query<person> (query::first_name.in ("John", "Jane"))); + print (r); + + r = db->query<person> (query::first_name.in_range (names, names_end)); + print (r); + + // Empty range. + // + r = db->query<person> (query::last_name == "Doe" && + query::first_name.in_range (names, names)); + assert (r.empty ()); + + t.commit (); + } + + // Test column-to-column comparison. + // + cout << "test 012" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::first_name == query::last_name)); + print (r); + t.commit (); + } + + // Test value_traits::value_type != value_traits::query_type. + // + cout << "test 013" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::middle_name == "Squeaky")); + print (r); + t.commit (); + } + + // Test that loading of the same object type during iteration does + // not invalidate the result. + // + cout << "test 014" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "Doe")); + + result::iterator i (r.begin ()); + assert (i != r.end ()); + ++i; + assert (i != r.end ()); + + { + unique_ptr<person> joe (db->load<person> (3)); + } + + { + person p (5, "Peter", "Peterson", 70, false, key3); + db->persist (p); + db->erase (p); + } + + // SQL Server does not support re-loading of an object with long data + // from a query result. + // + if (db_id != odb::id_mssql) + assert (i->last_name_ == "Doe"); // Actual load. + + // Overwrite object image again. + // + unique_ptr<person> joe (db->load<person> (3)); + person p; + i.load (p); + assert (p.last_name_ == "Doe"); + + t.commit (); + } + + // Test uncached result. + // + cout << "test 015" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "Doe", false)); + print (r); + t.commit (); + } + + // Test BLOB column operations. + // + cout << "test 016" << endl; + { + transaction t (db->begin ()); + + result r; + result::iterator i; + + // == + // + + // Oracle does not support LOB comparisons. + // +#if defined(MULTI_DATABASE) || !defined(DATABASE_ORACLE) + if (db_id != odb::id_oracle) + { + r = db->query<person> (query::public_key == key2); + + i = r.begin (); + assert (i != r.end ()); + + assert (*i->public_key_ == key2); + assert (++i == r.end ()); + } +#endif + + // is_null + // + r = db->query<person> (query::public_key.is_null ()); + + i = r.begin (); + assert (i != r.end ()); + + assert (i->first_name_ == "Johansen" && i->last_name_ == "Johansen"); + assert (++i == r.end ()); + + // is_not_null + // + r = db->query<person> (query::public_key.is_not_null ()); + + i = r.begin (); + assert (i != r.end ()); + + assert (i->first_name_ == "John" && i->last_name_ == "Doe"); + assert (++i != r.end ()); + + assert (i->first_name_ == "Jane" && i->last_name_ == "Doe"); + assert (++i != r.end ()); + + assert (i->first_name_ == "Joe" && i->last_name_ == "Dirt"); + assert (++i == r.end ()); + + t.commit (); + } + + // Test iterator::id(). + // + cout << "test 017" << endl; + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "Dirt")); + + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i.id () == 3); + + t.commit (); + } + + // Test empty result set. + // + cout << "test 018" << endl; + { + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "None")); + assert (r.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "None")); + assert (r.begin () == r.end ()); + t.commit (); + } + } + + // Test size() validity at the beginning/middle/end of result set. + // + cout << "test 019" << endl; + if (db_id != odb::id_sqlite && + db_id != odb::id_oracle && + db_id != odb::id_mssql) + { + { + transaction t (db->begin ()); + result r (db->query<person> ()); + assert (r.size () == 4); + result::iterator i (r.begin ()); + assert (r.size () == 4); + ++i; + ++i; + ++i; + assert (r.size () == 4); + ++i; + assert (r.size () == 4); + } + + { + transaction t (db->begin ()); + result r (db->query<person> (false)); + result::iterator i (r.begin ()); + ++i; + ++i; + r.cache (); // Cache in the middle. + assert (r.size () == 4); + ++i; + assert (r.size () == 4); + ++i; + assert (r.size () == 4); + } + + { + transaction t (db->begin ()); + result r (db->query<person> (false)); + result::iterator i (r.begin ()); + ++i; + ++i; + ++i; + r.cache (); // Cache at the end. + assert (r.size () == 4); + } + + { + transaction t (db->begin ()); + result r (db->query<person> (query::last_name == "None")); + assert (r.size () == 0); + for (result::iterator i (r.begin ()); i != r.end (); ++i) ; + assert (r.size () == 0); + } + } + + // Test like. + // + cout << "test 020" << endl; + { + transaction t (db->begin ()); + + result r (db->query<person> (query::first_name.like ("Jo%"))); + print (r); + + r = db->query<person> (!query::first_name.like ("Jo%")); + print (r); + + r = db->query<person> (query::first_name.like ("Jo!%", "!")); + print (r); + + // In Oracle one can only escape special characters (% and _). + // + string v; + if (db_id != odb::id_oracle) + v = "!Ja%"; + else + v = "Ja%"; + + r = db->query<person> (query::first_name.like (query::_ref (v), "!")); + print (r); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/query/basics/test.hxx b/odb-tests/common/query/basics/test.hxx new file mode 100644 index 0000000..239f6d6 --- /dev/null +++ b/odb-tests/common/query/basics/test.hxx @@ -0,0 +1,117 @@ +// file : common/query/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> +#include <memory> +#include <iostream> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +#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 + +typedef std::vector<char> buffer; +typedef odb::nullable<buffer> nullable_buffer; + +#pragma db object +struct person +{ + person (unsigned long id, + const std::string& fn, + const std::string& ln, + unsigned short age, + bool married, + const nullable_buffer& public_key = nullable_buffer ()) + : id_ (id), + first_name_ (fn), + last_name_ (ln), + age_ (age), + married_ (married), + public_key_ (public_key) + { + } + + person () + { + } + + #pragma db id + unsigned long id_; + + #pragma db column ("first") + std::string first_name_; + + #pragma db column ("middle") null + std::unique_ptr<std::string> middle_name_; + + #pragma db column ("last") + std::string last_name_; + + unsigned short age_; + bool married_; + + #pragma db column ("key") type(BLOB_TYPE) null + nullable_buffer public_key_; +}; + +inline std::ostream& +operator<< (std::ostream& os, const person& p) +{ + os << p.first_name_; + + if (p.middle_name_.get () != 0) + os << ' ' << *p.middle_name_; + + os << ' ' << p.last_name_ << ' ' << p.age_ << + (p.married_ ? " married" : " single"); + + return os; +} + +// Test member name conflicts (compilation-only test). +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db object + struct object + { + #pragma db id + int id; + }; + + #pragma db value + struct value + { + object* m_object; + }; + + #pragma db value + struct bar + { + value m_value; + }; + + #pragma db object + struct foo + { + #pragma db id + int id; + + bar m_value; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/query/basics/testscript b/odb-tests/common/query/basics/testscript new file mode 100644 index 0000000..d7dcf21 --- /dev/null +++ b/odb-tests/common/query/basics/testscript @@ -0,0 +1,170 @@ +# file : common/query/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + ++cat <<EOI >=output + test 001 + John Doe 30 married + Jane Doe 29 married + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + test 002 + Jane Doe 29 married + John Doe 30 married + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + test 003 + John Doe 30 married + + test 004 + John Doe 30 married + + test 005 + John Doe 30 married + + Joe Squeaky Dirt 31 single + + test 006 + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + test 007 + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + test 008 + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + + John Doe 30 married + Jane Doe 29 married + + test 009 + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + John Doe 30 married + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + test 010 + Jane Doe 29 married + + John Doe 30 married + Jane Doe 29 married + Joe Squeaky Dirt 31 single + + Jane Doe 29 married + + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + Jane Doe 29 married + John Doe 30 married + + test 011 + John Doe 30 married + Jane Doe 29 married + + John Doe 30 married + Jane Doe 29 married + Joe Squeaky Dirt 31 single + + test 012 + Johansen J Johansen 32 single + + test 013 + Joe Squeaky Dirt 31 single + + test 014 + test 015 + John Doe 30 married + Jane Doe 29 married + + test 016 + test 017 + test 018 + test 019 + test 020 + John Doe 30 married + Joe Squeaky Dirt 31 single + Johansen J Johansen 32 single + + Jane Doe 29 married + + + Jane Doe 29 married + + EOI + +test.redirects += >>>../output + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/query/one/buildfile b/odb-tests/common/query/one/buildfile new file mode 100644 index 0000000..76a36b0 --- /dev/null +++ b/odb-tests/common/query/one/buildfile @@ -0,0 +1,42 @@ +# file : common/query/one/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_query_one_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +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/query/one/driver.cxx b/odb-tests/common/query/one/driver.cxx new file mode 100644 index 0000000..4c3dcdc --- /dev/null +++ b/odb-tests/common/query/one/driver.cxx @@ -0,0 +1,205 @@ +// file : common/query/one/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test query one support. +// +// We assume that other tests in common/query/ exercise a variety of +// different kinds of queries. Here we are concerned with what is +// specific to query_one() and query_value(). +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + odb::database_id db_id (db->id ()); + + transaction t (db->begin ()); + + // query_one() + // + { + unique_ptr<object> o (db->query_one<object> ()); + assert (o.get () == 0); + } + + { + object o (4); + assert (!db->query_one<object> (o) && o.id_ == 4 && o.str_.empty ()); + } + + /* + { + object o (db->query_value<object> ()); + assert (false); + } + */ + + object o (1); + o.str_ = "value 1"; + db->persist (o); + + { + unique_ptr<object> o (db->query_one<object> ()); + assert (o.get () != 0 && o->str_ == "value 1"); + } + + { + object o; + assert (db->query_one<object> (o) && o.str_ == "value 1"); + } + + { + object o (db->query_value<object> ()); + assert (o.str_ == "value 1"); + } + + // query_one(const char*) + // + const char* q1_c (db_id == odb::id_oracle ? "\"id\" = 1" : "id = 1"); + const char* q0_c (db_id == odb::id_oracle ? "\"id\" = 2" : "id = 2"); + + { + unique_ptr<object> o (db->query_one<object> (q1_c)); + assert (o.get () != 0 && o->str_ == "value 1"); + } + + { + unique_ptr<object> o (db->query_one<object> (q0_c)); + assert (o.get () == 0); + } + + { + object o; + assert (db->query_one<object> (q1_c, o) && o.str_ == "value 1"); + } + + { + object o (4); + assert (!db->query_one<object> (q0_c, o) && + o.id_ == 4 && o.str_.empty ()); + } + + { + object o (db->query_value<object> (q1_c)); + assert (o.str_ == "value 1"); + } + + // query_one(std::string) + // + string q1_s (q1_c); + string q0_s (q0_c); + + { + unique_ptr<object> o (db->query_one<object> (q1_s)); + assert (o.get () != 0 && o->str_ == "value 1"); + } + + { + unique_ptr<object> o (db->query_one<object> (q0_s)); + assert (o.get () == 0); + } + + { + object o; + assert (db->query_one<object> (q1_s, o) && o.str_ == "value 1"); + } + + { + object o (4); + assert (!db->query_one<object> (q0_s, o) && + o.id_ == 4 && o.str_.empty ()); + } + + { + object o (db->query_value<object> (q1_s)); + assert (o.str_ == "value 1"); + } + + // query_one(odb::query) + // + typedef odb::query<object> query; + + query q1 (query::id == 1); + query q0 (query::id == 2); + + { + unique_ptr<object> o (db->query_one<object> (q1)); + assert (o.get () != 0 && o->str_ == "value 1"); + } + + { + unique_ptr<object> o (db->query_one<object> (q0)); + assert (o.get () == 0); + } + + { + object o; + assert (db->query_one<object> (q1, o) && o.str_ == "value 1"); + } + + { + object o (4); + assert (!db->query_one<object> (q0, o) && o.id_ == 4 && o.str_.empty ()); + } + + { + object o (db->query_value<object> (q1)); + assert (o.str_ == "value 1"); + } + + // Assertion on more than one element. + // + { + object o (2); + o.str_ = "value 2"; + db->persist (o); + } + + /* + { + unique_ptr<object> o (db->query_one<object> ()); + assert (false); + } + */ + + /* + { + object o; + db->query_one<object> (o); + assert (false); + } + */ + + /* + { + object o (db->query_value<object> ()); + assert (false); + } + */ + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/query/one/test.hxx b/odb-tests/common/query/one/test.hxx new file mode 100644 index 0000000..3008063 --- /dev/null +++ b/odb-tests/common/query/one/test.hxx @@ -0,0 +1,26 @@ +// file : common/query/one/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#pragma db object +struct object +{ + object (unsigned long id) + : id_ (id) + { + } + + object () + { + } + + #pragma db id + unsigned long id_; + std::string str_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/query/one/testscript b/odb-tests/common/query/one/testscript new file mode 100644 index 0000000..94ebcde --- /dev/null +++ b/odb-tests/common/query/one/testscript @@ -0,0 +1,53 @@ +# file : common/query/one/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/readonly/buildfile b/odb-tests/common/readonly/buildfile new file mode 100644 index 0000000..2d83cf1 --- /dev/null +++ b/odb-tests/common/readonly/buildfile @@ -0,0 +1,40 @@ +# file : common/readonly/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 readonly_ \ + --generate-schema + +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/readonly/driver.cxx b/odb-tests/common/readonly/driver.cxx new file mode 100644 index 0000000..3531e57 --- /dev/null +++ b/odb-tests/common/readonly/driver.cxx @@ -0,0 +1,324 @@ +// file : common/readonly/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test readonly members/objects. Also test that const members are +// treated as readonly. For other const member tests, see the const- +// member test. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/config.hxx> // MULTI_DATABASE +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Simple. + // + { + simple o (1, 1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.ro++; + const_cast<unsigned long&> (o.co)++; + o.rw++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load<simple> (1, o); + t.commit (); + } + + assert (o.ro == 1 && o.co == 1 && o.rw == 2); + } + + // Pointer. + // + { + pointer p (1, new pointer (2)); + unique_ptr<pointer> p1 (new pointer (3)); + + { + transaction t (db->begin ()); + db->persist (p); + db->persist (p.ro); + db->persist (*p1); + t.commit (); + } + + delete p.ro; + p.ro = p1.release (); + const_cast<pointer*&> (p.co) = p.ro; + p.rw = p.ro; + + { + transaction t (db->begin ()); + db->update (p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<pointer> p (db->load<pointer> (1)); + t.commit (); + + assert (p->ro->id == 2 && p->co->id == 2 && p->rw->id == 3); + } + } + + // Composite. + // + { + composite o (1, 1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.ro.v++; + o.ro.ro++; + const_cast<unsigned long&> (o.ro.co)++; + o.ro.rw++; + + value& co (const_cast<value&> (o.co)); + co.v++; + co.ro++; + const_cast<unsigned long&> (co.co)++; + co.rw++; + + o.rw.v++; + o.rw.ro++; + const_cast<unsigned long&> (o.rw.co)++; + o.rw.rw++; + + o.v.v++; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load<composite> (1, o); + t.commit (); + } + + assert (o.ro.v == 1 && + o.ro.ro == 1 && + o.ro.co == 1 && + o.ro.rw == 1 && + + o.co.v == 1 && + o.co.ro == 1 && + o.co.co == 1 && + o.co.rw == 1 && + + o.rw.v == 1 && + o.rw.ro == 1 && + o.rw.co == 1 && + o.rw.rw == 2 && + + o.v.v == 1); + } + + // Container. + // + { + typedef vector<unsigned long> ulongs; + + container o (1); + + o.ro.push_back (1); + o.ro.push_back (2); + + ulongs& co (const_cast<ulongs&> (o.co)); + co.push_back (1); + co.push_back (2); + + o.rw.push_back (1); + o.rw.push_back (2); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.ro[0]++; + o.ro.pop_back (); + + co[0]++; + co.pop_back (); + + o.rw[0]++; + o.rw.pop_back (); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load<container> (1, o); + t.commit (); + } + + assert (o.ro.size () == 2 && o.ro[0] == 1 && o.ro[1] == 2 && + o.co.size () == 2 && o.co[0] == 1 && o.co[1] == 2 && + o.rw.size () == 1 && o.rw[0] == 2); + } + + // Readonly object. + // + { +#ifndef MULTI_DATABASE + typedef odb::object_traits_impl<simple_object, odb::id_common> so_traits; + typedef odb::object_traits_impl<ro_object, odb::id_common> ro_traits; + typedef odb::object_traits_impl<rw_object, odb::id_common> rw_traits; + + assert (so_traits::column_count == + so_traits::id_column_count + so_traits::readonly_column_count); + + assert (ro_traits::column_count == + ro_traits::id_column_count + ro_traits::readonly_column_count); + + assert (rw_traits::column_count != + rw_traits::id_column_count + rw_traits::readonly_column_count); +#endif + + simple_object so (1, 1); + ro_object ro_o (1, 1); + rw_object rw_o (1, 1); + + ro_o.cr.push_back (1); + ro_o.cr.push_back (2); + + rw_o.cr.push_back (1); + rw_o.cr.push_back (2); + + { + transaction t (db->begin ()); + db->persist (so); + db->persist (ro_o); + db->persist (rw_o); + t.commit (); + } + + rw_o.sv++; + rw_o.rw_sv++; + + { + transaction t (db->begin ()); + //db->update (so); // Compile error. + //db->update (ro_o); // Compile error. + db->update (rw_o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load (1, so); + db->load (1, ro_o); + db->load (1, rw_o); + t.commit (); + } + + assert (rw_o.sv == 1 && rw_o.rw_sv == 2); + } + + // Readonly object. + // + { + wrapper o (1, 1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + *o.pl = 2; + *o.cpl = 2; + o.pcl.reset (new unsigned long (2)); + const_cast<unsigned long&> (*o.cpcl) = 2; + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->load<wrapper> (1, o); + t.commit (); + } + + assert (*o.pl == 2 && *o.cpl == 2 && *o.pcl == 2 && *o.cpcl == 1); + } + + // Readonly object with auto id. + // + { + ro_auto o1 (1); + ro_auto o2 (2); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<ro_auto> p1 (db->load<ro_auto> (o1.id)); + unique_ptr<ro_auto> p2 (db->load<ro_auto> (o2.id)); + t.commit (); + + assert (p1->num == o1.num); + assert (p2->num == o2.num); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/readonly/test.hxx b/odb-tests/common/readonly/test.hxx new file mode 100644 index 0000000..45797b6 --- /dev/null +++ b/odb-tests/common/readonly/test.hxx @@ -0,0 +1,225 @@ +// file : common/readonly/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> +#include <memory> // std::unique_ptr + +#include <odb/core.hxx> + +// Simple readonly object. +// +#pragma db object +struct simple +{ + simple (unsigned long i, unsigned long x) + : id (i), ro (x), co (x), rw (x) + { + } + + simple (): co (0) {} + + #pragma db id + unsigned long id; + + #pragma db readonly + unsigned long ro; + + const unsigned long co; + + unsigned long rw; +}; + +// Readonly pointer. +// +#pragma db object +struct pointer +{ + pointer (unsigned long i, pointer* p = 0): id (i), ro (p), co (p), rw (p) {} + pointer (): ro (0), co (0), rw (0) {} + + ~pointer () + { + delete ro; + + if (co != ro) + delete co; + + if (rw != ro && rw != co) + delete rw; + } + + #pragma db id + unsigned long id; + + #pragma db readonly + pointer* ro; + + pointer* const co; + + pointer* rw; +}; + +// Composite readonly value as well as simple readonly value inside +// a composite. +// +#pragma db value readonly +struct ro_value +{ + ro_value () {} + ro_value (unsigned long x): v (x) {} + + unsigned long v; +}; + +#pragma db value +struct value: ro_value +{ + value (): co (0) {} + value (unsigned long x): ro_value (x), ro (x), co (x), rw (x) {} + + #pragma db readonly + unsigned long ro; + + const unsigned long co; + + unsigned long rw; +}; + +#pragma db object +struct composite +{ + composite (unsigned long i, unsigned long x) + : id (i), ro (x), co (x), rw (x), v (x) + { + } + + composite () {} + + #pragma db id + unsigned long id; + + #pragma db readonly + value ro; + + const value co; + + value rw; + ro_value v; +}; + +// Readonly container. +// +#pragma db object +struct container +{ + container (unsigned long i): id (i) {} + container () {} + + #pragma db id + unsigned long id; + + #pragma db readonly + std::vector<unsigned long> ro; + + const std::vector<unsigned long> co; + + std::vector<unsigned long> rw; +}; + +// Readonly object. +// +#pragma db object readonly +struct simple_object +{ + simple_object (unsigned long i, unsigned long x): id (i), sv (x) {} + simple_object () {} + + #pragma db id + unsigned long id; + + unsigned long sv; +}; + +#pragma db object +struct object +{ + object (unsigned long i, unsigned long x): id (i), sv (x) {} + object () {} + + #pragma db id + unsigned long id; + + unsigned long sv; +}; + +#pragma db object readonly +struct ro_object: object +{ + ro_object (unsigned long i, unsigned long x) + : object (i, x), cv (x) + { + } + + ro_object () {} + + value cv; + std::vector<unsigned long> cr; +}; + +#pragma db object +struct rw_object: ro_object +{ + rw_object (unsigned long i, unsigned long x) + : ro_object (i, x), rw_sv (x) + { + } + + rw_object () {} + + unsigned long rw_sv; +}; + +// Readonly wrappers. Here we make sure that only const wrappers with +// const wrapped types are automatically treated as readonly. +// +#pragma db object +struct wrapper +{ + wrapper (unsigned long i, unsigned long x) + : id (i), + pl (new unsigned long (x)), + cpl (new unsigned long (x)), + pcl (new unsigned long (x)), + cpcl (new unsigned long (x)) + { + } + + wrapper () {} + + #pragma db id + unsigned long id; + + std::unique_ptr<unsigned long> pl; + const std::unique_ptr<unsigned long> cpl; + std::unique_ptr<const unsigned long> pcl; + const std::unique_ptr<const unsigned long> cpcl; +}; + +// Readonly object with auto id. +// +#pragma db object readonly +struct ro_auto +{ + ro_auto (unsigned long n): num (n) {} + ro_auto () {} + + #pragma db id auto + unsigned long id; + + unsigned long num; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/readonly/testscript b/odb-tests/common/readonly/testscript new file mode 100644 index 0000000..491b55b --- /dev/null +++ b/odb-tests/common/readonly/testscript @@ -0,0 +1,53 @@ +# file : common/readonly/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/relationship/basics/buildfile b/odb-tests/common/relationship/basics/buildfile new file mode 100644 index 0000000..d7bbb7e --- /dev/null +++ b/odb-tests/common/relationship/basics/buildfile @@ -0,0 +1,41 @@ +# file : common/relationship/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_rel_basics_ \ + --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/relationship/basics/driver.cxx b/odb-tests/common/relationship/basics/driver.cxx new file mode 100644 index 0000000..e27c127 --- /dev/null +++ b/odb-tests/common/relationship/basics/driver.cxx @@ -0,0 +1,150 @@ +// file : common/relationship/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test object relationships. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + aggr a ("aggr"); + a.o1 = new obj1 ("o1", "obj1"); + a.o2.reset (new obj2 ("obj2")); + + a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 1"))); + a.v2.push_back (0); + a.v2.push_back (obj2_ptr (new obj2 ("v1 obj2 2"))); + + a.o3.reset (new obj3 ("obj3")); + + a.c.num = 123; + a.c.o3.reset (new obj3 ("c")); + + a.cv.push_back (comp (234, obj3_ptr (new obj3 ("cv 0")))); + a.cv.push_back (comp (235, obj3_ptr ())); + a.cv.push_back (comp (236, obj3_ptr (new obj3 ("cv 2")))); + + a.v1.push_back (new obj1 ("v1 0", "v1 0")); + a.v1.push_back (0); + a.v1.push_back (new obj1 ("v1 2", "v1 2")); + + // Set cannot contain NULL pointers. + // + a.s1.insert (new obj1 ("s1 0", "s1 0")); + a.s1.insert (new obj1 ("s1 2", "s1 2")); + + a.m1[0] = new obj1 ("m1 0", "m1 0"); + a.m1[1] = 0; + a.m1[2] = new obj1 ("m1 2", "m1 2"); + + // persist + // + { + transaction t (db->begin ()); + db->persist (a.o1); + db->persist (a.o2); + + for (obj2_vec::iterator i (a.v2.begin ()); i != a.v2.end (); ++i) + if (*i) + db->persist (*i); + + db->persist (a.o3); + + db->persist (a.c.o3); + + for (comp_vec::iterator i (a.cv.begin ()); i != a.cv.end (); ++i) + if (i->o3) + db->persist (i->o3); + + for (obj1_vec::iterator i (a.v1.begin ()); i != a.v1.end (); ++i) + if (*i) + db->persist (*i); + + for (obj1_set::iterator i (a.s1.begin ()); i != a.s1.end (); ++i) + if (*i) + db->persist (*i); + + for (obj1_map::iterator i (a.m1.begin ()); i != a.m1.end (); ++i) + if (i->second) + db->persist (i->second); + + db->persist (a); + t.commit (); + } + + // load & compare + // + { + transaction t (db->begin ()); + unique_ptr<aggr> a1 (db->load<aggr> (a.id)); + t.commit (); + + assert (*a1 == a); + } + + // query + // + typedef odb::query<aggr> query; + typedef odb::result<aggr> result; + + { + transaction t (db->begin ()); + + result r (db->query<aggr> (query::o1->str == "obj1")); + assert (!r.empty ()); + assert (r.begin ()->o1->id == a.o1->id); + assert (size (r) == 1); + + t.commit (); + } + + // Test NULL pointer. + // + delete a.o1; + a.o1 = 0; + a.o2.reset (); + a.o3.reset (); + + { + transaction t (db->begin ()); + db->update (a); + t.commit (); + } + + // load & compare + // + { + transaction t (db->begin ()); + unique_ptr<aggr> a1 (db->load<aggr> (a.id)); + t.commit (); + + assert (*a1 == a); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/relationship/basics/test.hxx b/odb-tests/common/relationship/basics/test.hxx new file mode 100644 index 0000000..e2d3936 --- /dev/null +++ b/odb-tests/common/relationship/basics/test.hxx @@ -0,0 +1,260 @@ +// file : common/relationship/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <set> +#include <map> +#include <vector> +#include <string> +#include <memory> + +#include <odb/core.hxx> + +// Raw pointer. +// +#pragma db object pointer(obj1*) +struct obj1 +{ + obj1 () {} + obj1 (const std::string& i, const std::string& s): id (i), str (s) {} + + #pragma db id + std::string id; + std::string str; +}; + +inline bool +operator== (const obj1& x, const obj1& y) +{ + return x.id == y.id && x.str == y.str; +} + +// vector +// +typedef std::vector<obj1*> obj1_vec; + +inline bool +operator== (const obj1_vec& x, const obj1_vec& y) +{ + if (x.size () != y.size ()) + return false; + + for (obj1_vec::size_type i (0); i < x.size (); ++i) + if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i])) + return false; + + return true; +} + +// set +// +struct obj1_cmp +{ + bool + operator() (obj1* x, obj1* y) const + { + return (!x || !y) ? x < y : x->id < y->id; + } +}; + +typedef std::set<obj1*, obj1_cmp> obj1_set; + +inline bool +operator== (const obj1_set& x, const obj1_set& y) +{ + if (x.size () != y.size ()) + return false; + + for (obj1_set::const_iterator i (x.begin ()); i != x.end (); ++i) + { + obj1_set::const_iterator j (y.find (*i)); + + if (j == y.end ()) + return false; + + obj1* x (*i); + obj1* y (*j); + + if (!(x ? (y && *x == *y) : !y)) + return false; + } + + return true; +} + +// map +// +typedef std::map<int, obj1*> obj1_map; + +inline bool +operator== (const obj1_map& x, const obj1_map& y) +{ + if (x.size () != y.size ()) + return false; + + for (obj1_map::const_iterator i (x.begin ()); i != x.end (); ++i) + { + obj1_map::const_iterator j (y.find (i->first)); + + if (j == y.end ()) + return false; + + obj1* x (i->second); + obj1* y (j->second); + + if (!(x ? (y && *x == *y) : !y)) + return false; + } + + return true; +} + +// auto_ptr/unique_ptr +// +struct obj2; + +typedef std::unique_ptr<obj2> obj2_ptr; + +#pragma db object pointer(obj2_ptr) +struct obj2 +{ + obj2 () {} + obj2 (const std::string& s): str (s) {} + + #pragma db id auto + unsigned long id; + + std::string str; +}; + +inline bool +operator== (const obj2& x, const obj2& y) +{ + return x.id == y.id && x.str == y.str; +} + +typedef std::vector<obj2_ptr> obj2_vec; + +inline bool +operator== (const obj2_vec& x, const obj2_vec& y) +{ + if (x.size () != y.size ()) + return false; + + for (obj2_vec::size_type i (0); i < x.size (); ++i) + if (!(x[i] ? (y[i] && *x[i] == *y[i]) : !y[i])) + return false; + + return true; +} + +// shared_ptr +// +struct obj3; + +typedef std::shared_ptr<obj3> obj3_ptr; + +#pragma db object pointer(obj3_ptr) +struct obj3 +{ + obj3 () {} + obj3 (const std::string& s): str (s) {} + + #pragma db id auto + unsigned long id; + + std::string str; +}; + +inline bool +operator== (const obj3& x, const obj3& y) +{ + return x.id == y.id && x.str == y.str; +} + +// composite +// +#pragma db value +struct comp +{ + comp () = default; + comp (int n, obj3_ptr o): num (n), o3 (o) {} + + int num; + obj3_ptr o3; +}; + +inline bool +operator== (const comp& x, const comp& y) +{ + return x.num == y.num && + (x.o3 ? (y.o3 && *x.o3 == *y.o3) : !y.o3); +} + +typedef std::vector<comp> comp_vec; + +// +// +#pragma db object +struct aggr +{ + aggr (): o1 (0) {} + aggr (const std::string& s): o1 (0), str (s) {} + + ~aggr () + { + delete o1; + + for (obj1_vec::iterator i (v1.begin ()); i != v1.end (); ++i) + delete *i; + + for (obj1_set::iterator i (s1.begin ()); i != s1.end (); ++i) + delete *i; + + for (obj1_map::iterator i (m1.begin ()); i != m1.end (); ++i) + delete i->second; + } + + #pragma db id auto + unsigned long id; + + obj1* o1; + + obj2_ptr o2; // std::auto_ptr or std::unique_ptr + obj2_vec v2; + + obj3_ptr o3; + comp c; + comp_vec cv; + + obj1_vec v1; + obj1_set s1; + obj1_map m1; + + std::string str; + +private: + aggr (const aggr&); + aggr& operator= (const aggr&); +}; + +inline bool +operator== (const aggr& x, const aggr& y) +{ + return + x.id == y.id && + (x.o1 ? (y.o1 && *x.o1 == *y.o1) : !y.o1) && + (x.o2.get () ? (y.o2.get () && *x.o2 == *y.o2) : !y.o2.get ()) && + x.v2 == y.v2 && + (x.o3.get () ? (y.o3.get () && *x.o3 == *y.o3) : !y.o3.get ()) && + x.c == y.c && + x.cv == y.cv && + x.v1 == y.v1 && + x.s1 == y.s1 && + x.m1 == y.m1 && + x.str == y.str; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/relationship/basics/testscript b/odb-tests/common/relationship/basics/testscript new file mode 100644 index 0000000..7ed44b4 --- /dev/null +++ b/odb-tests/common/relationship/basics/testscript @@ -0,0 +1,53 @@ +# file : common/relationship/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/relationship/on-delete/buildfile b/odb-tests/common/relationship/on-delete/buildfile new file mode 100644 index 0000000..5cdb585 --- /dev/null +++ b/odb-tests/common/relationship/on-delete/buildfile @@ -0,0 +1,46 @@ +# file : common/relationship/on-delete/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_rel_on_d_ \ + --generate-schema + +if $mysql + odb_options += --fkeys-deferrable-mode mysql:not_deferrable + +if $mssql + odb_options += --fkeys-deferrable-mode mssql:not_deferrable + +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/relationship/on-delete/driver.cxx b/odb-tests/common/relationship/on-delete/driver.cxx new file mode 100644 index 0000000..eec57cf --- /dev/null +++ b/odb-tests/common/relationship/on-delete/driver.cxx @@ -0,0 +1,82 @@ +// file : common/relationship/on-delete/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test ON DELETE functionality. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + object o; + + cascade c; + c.p = &o; + + cascade_cont cc; + cc.p.push_back (&o); + + set_null n; + n.p = &o; + + set_null_cont nc; + nc.p.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (c); + db->persist (cc); + db->persist (n); + db->persist (nc); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + + { + transaction t (db->begin ()); + assert (db->find<cascade> (c.id) == 0); + + unique_ptr<cascade_cont> pcc (db->load<cascade_cont> (cc.id)); + assert (pcc->p.empty ()); + + unique_ptr<set_null> pn (db->load<set_null> (n.id)); + assert (pn->p == 0); + + unique_ptr<set_null_cont> pnc (db->load<set_null_cont> (nc.id)); + assert (pnc->p.size () == 1 && pnc->p[0] == 0); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/relationship/on-delete/test.hxx b/odb-tests/common/relationship/on-delete/test.hxx new file mode 100644 index 0000000..841acd9 --- /dev/null +++ b/odb-tests/common/relationship/on-delete/test.hxx @@ -0,0 +1,58 @@ +// file : common/relationship/on-delete/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <vector> + +#include <odb/core.hxx> + +#pragma db object +struct object +{ + #pragma db id auto + unsigned long id; +}; + +#pragma db object +struct cascade +{ + #pragma db id auto + unsigned long id; + + #pragma db on_delete(cascade) + object* p; +}; + +#pragma db object +struct cascade_cont +{ + #pragma db id auto + unsigned long id; + + #pragma db on_delete(cascade) + std::vector<object*> p; +}; + +#pragma db object +struct set_null +{ + #pragma db id auto + unsigned long id; + + #pragma db on_delete(set_null) + object* p; +}; + +#pragma db object +struct set_null_cont +{ + #pragma db id auto + unsigned long id; + + #pragma db on_delete(set_null) + std::vector<object*> p; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/relationship/on-delete/testscript b/odb-tests/common/relationship/on-delete/testscript new file mode 100644 index 0000000..5777084 --- /dev/null +++ b/odb-tests/common/relationship/on-delete/testscript @@ -0,0 +1,53 @@ +# file : common/relationship/on-delete/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/relationship/query/buildfile b/odb-tests/common/relationship/query/buildfile new file mode 100644 index 0000000..b70edc4 --- /dev/null +++ b/odb-tests/common/relationship/query/buildfile @@ -0,0 +1,42 @@ +# file : common/relationship/query/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_rel_query_ \ + --generate-schema \ + --generate-query \ + --generate-session + +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/relationship/query/driver.cxx b/odb-tests/common/relationship/query/driver.cxx new file mode 100644 index 0000000..20d5370 --- /dev/null +++ b/odb-tests/common/relationship/query/driver.cxx @@ -0,0 +1,168 @@ +// file : common/relationship-query/query/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test relationship queries. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // + // + { + shared_ptr<country> ca (new country ("CA", "Canada")); + shared_ptr<country> za (new country ("ZA", "South Africa")); + shared_ptr<country> us (new country ("US", "United States")); + shared_ptr<country> se (new country ("SE", "Sweden")); + + shared_ptr<employer> st (new employer ("Simple Tech, Inc", ca)); + shared_ptr<employer> ct (new employer ("Complex Tech, Inc", us)); + + // person + // + shared_ptr<person> p1 ( + new person (1, "John", "Doe", 30, ca, true, za)); + + shared_ptr<person> p2 ( + new person (2, "Jane", "Doe", 29, za, false, us)); + p2->husband = p1; + + shared_ptr<person> p3 ( + new person (3, "Joe", "Dirt", 31, us, true, us)); + + shared_ptr<person> p4 ( + new person (4, "Johan", "Johansen", 32, se, false, ca)); + + // employee + // + shared_ptr<employee> e1 ( + new employee (1, "John", "Doe", 30, ca, true, za, st)); + + shared_ptr<employee> e2 ( + new employee (2, "Jane", "Doe", 29, za, false, us, ct)); + e2->husband = p1; + + shared_ptr<employee> e3 ( + new employee (3, "Joe", "Dirt", 31, us, true, us, st)); + + shared_ptr<employee> e4 ( + new employee (4, "Johan", "Johansen", 32, se, false, ca, ct)); + + transaction t (db->begin ()); + db->persist (ca); + db->persist (za); + db->persist (us); + db->persist (se); + + db->persist (st); + db->persist (ct); + + db->persist (p1); + db->persist (p2); + db->persist (p3); + db->persist (p4); + + db->persist (e1); + db->persist (e2); + db->persist (e3); + db->persist (e4); + t.commit (); + } + + typedef odb::query<person> p_query; + typedef odb::result<person> p_result; + + typedef odb::query<employee> e_query; + typedef odb::result<employee> e_result; + + // Make sure we have an independent JOIN for each relationship. + // + { + session s; + transaction t (db->begin ()); + + p_result pr (db->query<person> ( + p_query::residence.location->code == "ZA")); + assert (size (pr) == 1); + + e_result er (db->query<employee> ( + e_query::residence.location->code == "ZA")); + assert (size (er) == 1); + + t.commit (); + } + + // Test Self-JOIN. + // + { + session s; + transaction t (db->begin ()); + + p_result pr (db->query<person> (p_query::husband->last_name == "Doe")); + assert (size (pr) == 1); + + e_result er (db->query<employee> (e_query::husband->last_name == "Doe")); + assert (size (er) == 1); + + t.commit (); + } + + // Test query conditions from both base and derived. + // + { + session s; + transaction t (db->begin ()); + + e_result r ( + db->query<employee> ( + e_query::employed_by->name == "Simple Tech, Inc" && + e_query::nationality->code == "US")); + + assert (size (r) == 1); + + t.commit (); + } + + // Test second-level pointers. + // + { + session s; + transaction t (db->begin ()); + + p_result r ( + db->query<person> ( + p_query::husband->residence.location == "CA")); + + assert (size (r) == 1); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/relationship/query/test.hxx b/odb-tests/common/relationship/query/test.hxx new file mode 100644 index 0000000..c6e2d6d --- /dev/null +++ b/odb-tests/common/relationship/query/test.hxx @@ -0,0 +1,140 @@ +// file : common/relationship-query/query/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <memory> + +#include <odb/core.hxx> + +using std::shared_ptr; + +struct country; + +#pragma db value +struct residence_info +{ + residence_info (bool p, shared_ptr<country> l) + : permanent (p), location (l) + { + } + + residence_info () + { + } + + bool permanent; + + #pragma db not_null + shared_ptr<country> location; +}; + +#pragma db object pointer(shared_ptr) +struct person +{ + person (unsigned long i, + const std::string& fn, + const std::string& ln, + unsigned short a, + shared_ptr<country> r, + bool p, + shared_ptr<country> n) + : id (i), + first_name (fn), + last_name (ln), + age (a), + residence (p, r), + nationality (n) + { + } + + person () + { + } + + #pragma db id + unsigned long id; + + #pragma db column ("first") + std::string first_name; + + #pragma db column ("last") + std::string last_name; + + unsigned short age; + + residence_info residence; + + #pragma db not_null + shared_ptr<country> nationality; + + shared_ptr<person> husband; // Self-join. +}; + +struct employer; + +#pragma db object pointer(shared_ptr) +struct employee: person +{ + employee (unsigned long i, + const std::string& fn, + const std::string& ln, + unsigned short a, + shared_ptr<country> r, + bool p, + shared_ptr<country> n, + shared_ptr<employer> e) + : person (i, fn, ln, a, r, p, n), + employed_by (e) + { + } + + employee () + { + } + + shared_ptr<employer> employed_by; +}; + +#pragma db object pointer(shared_ptr) +struct employer +{ + employer (const std::string& n, shared_ptr<country> nat) + : name (n), nationality (nat) + { + } + + employer () + { + } + + #pragma db id + std::string name; + + // The same member name and type as in person (test JOIN alias). + // + #pragma db not_null + shared_ptr<country> nationality; +}; + +#pragma db object pointer(shared_ptr) +struct country +{ + country (const std::string& c, const std::string& n) + : code (c), name (n) + { + } + + country () + { + } + + #pragma db id + std::string code; // ISO 2-letter country code. + + std::string name; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/relationship/query/testscript b/odb-tests/common/relationship/query/testscript new file mode 100644 index 0000000..41e959b --- /dev/null +++ b/odb-tests/common/relationship/query/testscript @@ -0,0 +1,53 @@ +# file : common/relationship/query/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/schema/embedded/basics/buildfile b/odb-tests/common/schema/embedded/basics/buildfile new file mode 100644 index 0000000..0cfe85e --- /dev/null +++ b/odb-tests/common/schema/embedded/basics/buildfile @@ -0,0 +1,42 @@ +# file : common/schema/embedded/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 schema_embd_bscs_ \ + --generate-schema \ + --schema-format embedded \ + --schema-name test + +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/schema/embedded/basics/driver.cxx b/odb-tests/common/schema/embedded/basics/driver.cxx new file mode 100644 index 0000000..7ba2bce --- /dev/null +++ b/odb-tests/common/schema/embedded/basics/driver.cxx @@ -0,0 +1,59 @@ +// file : common/schema/embedded/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test basic embedded schema functionality. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv, false)); + + // Create the database schema. + // + { + connection_ptr c (db->connection ()); + + // Temporarily disable foreign key constraints for SQLite. + // + if (db->id () == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=OFF"); + + assert (schema_catalog::exists (*db, "test")); + assert (!schema_catalog::exists (*db, "test1")); + assert (!schema_catalog::exists (*db, "")); + + transaction t (c->begin ()); + schema_catalog::create_schema (*db, "test"); + t.commit (); + + if (db->id () == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=ON"); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/schema/embedded/basics/test.hxx b/odb-tests/common/schema/embedded/basics/test.hxx new file mode 100644 index 0000000..43331f9 --- /dev/null +++ b/odb-tests/common/schema/embedded/basics/test.hxx @@ -0,0 +1,23 @@ +// file : common/schema/embedded/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +#pragma db object +struct object +{ + #pragma db auto id + unsigned long id; + + std::string str; + + std::vector<int> nums; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/schema/embedded/basics/testscript b/odb-tests/common/schema/embedded/basics/testscript new file mode 100644 index 0000000..6affa13 --- /dev/null +++ b/odb-tests/common/schema/embedded/basics/testscript @@ -0,0 +1,49 @@ +# file : common/schema/embedded/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../../../mssql.testscript + + $* +} diff --git a/odb-tests/common/schema/embedded/order/.gitignore b/odb-tests/common/schema/embedded/order/.gitignore new file mode 100644 index 0000000..5d39d39 --- /dev/null +++ b/odb-tests/common/schema/embedded/order/.gitignore @@ -0,0 +1,6 @@ +# ODB-generated files. +# +test1-odb.?xx +test1-odb-*.?xx +test2-odb.?xx +test2-odb-*.?xx diff --git a/odb-tests/common/schema/embedded/order/buildfile b/odb-tests/common/schema/embedded/order/buildfile new file mode 100644 index 0000000..b2fac0b --- /dev/null +++ b/odb-tests/common/schema/embedded/order/buildfile @@ -0,0 +1,48 @@ +# file : common/schema/embedded/order/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = test1 test2 + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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 schema_embd_ordr_ \ + --generate-schema \ + --schema-format embedded + +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/schema/embedded/order/driver.cxx b/odb-tests/common/schema/embedded/order/driver.cxx new file mode 100644 index 0000000..fde5e96 --- /dev/null +++ b/odb-tests/common/schema/embedded/order/driver.cxx @@ -0,0 +1,65 @@ +// file : common/schema/embedded/order/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test statement execution order in embedded schemas. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/common.hxx> + +#include "test1.hxx" +#include "test2.hxx" + +#include "test1-odb.hxx" +#include "test2-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv, false)); + odb::database_id db_id (db->id ()); + + // Create the database schema. + // + { + connection_ptr c (db->connection ()); + + // Temporarily disable foreign key constraints for MySQL and SQLite. + // For MySQL we can actually create the tables in any order. It is + // dropping them that's the problem (there is no IF EXISTS). + // + if (db_id == odb::id_mysql) + c->execute ("SET FOREIGN_KEY_CHECKS=0"); + else if (db_id == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=OFF"); + + transaction t (c->begin ()); + schema_catalog::create_schema (*db); + t.commit (); + + if (db_id == odb::id_mysql) + c->execute ("SET FOREIGN_KEY_CHECKS=1"); + else if (db_id == odb::id_sqlite) + c->execute ("PRAGMA foreign_keys=ON"); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/schema/embedded/order/test1.hxx b/odb-tests/common/schema/embedded/order/test1.hxx new file mode 100644 index 0000000..b35074b --- /dev/null +++ b/odb-tests/common/schema/embedded/order/test1.hxx @@ -0,0 +1,23 @@ +// file : common/schema/embedded/order/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object polymorphic +struct base +{ + virtual + ~base () {} + + #pragma db auto id + unsigned long id; + + std::string str; +}; + +#endif // TEST1_HXX diff --git a/odb-tests/common/schema/embedded/order/test2.hxx b/odb-tests/common/schema/embedded/order/test2.hxx new file mode 100644 index 0000000..b4e6d20 --- /dev/null +++ b/odb-tests/common/schema/embedded/order/test2.hxx @@ -0,0 +1,17 @@ +// file : common/schema/embedded/order/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#include <odb/core.hxx> + +#include "test1.hxx" + +#pragma db object +struct derived: base +{ + int num; +}; + +#endif // TEST2_HXX diff --git a/odb-tests/common/schema/embedded/order/testscript b/odb-tests/common/schema/embedded/order/testscript new file mode 100644 index 0000000..131b3fa --- /dev/null +++ b/odb-tests/common/schema/embedded/order/testscript @@ -0,0 +1,49 @@ +# file : common/schema/embedded/order/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../../../mssql.testscript + + $* +} diff --git a/odb-tests/common/schema/namespace/buildfile b/odb-tests/common/schema/namespace/buildfile new file mode 100644 index 0000000..fa496d7 --- /dev/null +++ b/odb-tests/common/schema/namespace/buildfile @@ -0,0 +1,41 @@ +# file : common/schema/namespace/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 schema_ns_ \ + --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/schema/namespace/driver.cxx b/odb-tests/common/schema/namespace/driver.cxx new file mode 100644 index 0000000..25515f2 --- /dev/null +++ b/odb-tests/common/schema/namespace/driver.cxx @@ -0,0 +1,113 @@ +// file : common/schema/namespace/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test database schemas (aka database namespaces). +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test database schema (aka database namespace). + // + using ns::object2; + + object2 o2; + o2.id = "aaa"; + o2.nums.push_back (1); + o2.nums.push_back (2); + o2.nums.push_back (3); + o2.obj1 = new object1; + o2.obj1->str = "aaa"; + + { + transaction t (db->begin ()); + db->persist (o2.obj1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> p2 (db->load<object2> ("aaa")); + t.commit (); + + assert (o2 == *p2); + } + + { + typedef odb::query<object2> query; + typedef odb::result<object2> result; + + transaction t (db->begin ()); + + { + result r (db->query<object2> (query::id == "aaa")); + assert (size (r) == 1); + } + + { + result r (db->query<object2> (query::obj1->str == "aaa")); + assert (size (r) == 1); + } + + t.commit (); + } + + { + typedef odb::query<object_view> query; + typedef odb::result<object_view> result; + + transaction t (db->begin ()); + + result r (db->query<object_view> (query::object2::id == "aaa")); + + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->id2 == "aaa" && i->str == "aaa"); + assert (++i == r.end ()); + + t.commit (); + } + + { + typedef odb::result<table_view> result; + + transaction t (db->begin ()); + + result r (db->query<table_view> ()); + + result::iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->str == "aaa"); + assert (++i == r.end ()); + + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/schema/namespace/test.hxx b/odb-tests/common/schema/namespace/test.hxx new file mode 100644 index 0000000..0b1844a --- /dev/null +++ b/odb-tests/common/schema/namespace/test.hxx @@ -0,0 +1,158 @@ +// file : common/schema/namespace/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +// Table names. +// +#pragma db object table("TABLE_EXPLICIT") +struct table_explicit +{ + #pragma db id + unsigned long id_; +}; + +#pragma db object +struct table_implicit +{ + #pragma db id + unsigned long id_; +}; + +// Column names. +// +#pragma db object +struct column +{ + #pragma db id + int m1; + + #pragma db column("foo") + int m2; + + int m_m3; + int _m4; + int m5_; + int m_; + int m__; +}; + +// Column types. +// +#pragma db object +struct type +{ + #pragma db id + std::string id; + + // Test default C++ to DB type mapping. + // + bool b; + char c; + signed char sc; + unsigned char uc; + short s; + unsigned short us; + int i; + unsigned int ui; + long l; + unsigned long ul; + long long ll; + unsigned long long ull; + float f; + double d; + std::string str; + + #pragma db type("INTEGER") + bool m1; + + #pragma db transient + char* m2; +}; + +// Test database schema (aka database namespace). +// +#ifdef ODB_COMPILER +#if defined (ODB_DATABASE_MYSQL) +//# define DB_SCHEMA "odb_test" +# define DB_SCHEMA "" +#elif defined (ODB_DATABASE_SQLITE) +# define DB_SCHEMA "main" +#elif defined (ODB_DATABASE_PGSQL) +# define DB_SCHEMA "public" +#elif defined (ODB_DATABASE_ORACLE) +//# define DB_SCHEMA "ODB_TEST" +# define DB_SCHEMA "" +#elif defined(ODB_DATABASE_MSSQL) +# define DB_SCHEMA "dbo" +#elif defined(ODB_DATABASE_COMMON) +# define DB_SCHEMA "dummy" +#else +# error unknown database +#endif +#endif + +namespace ns {typedef int my_int;} // Original. + +#pragma db object table(DB_SCHEMA."object_1") +struct object1 +{ + #pragma db id auto + unsigned long id; + + #pragma db column("str") + std::string str; +}; + +inline bool +operator== (const object1& x, const object1& y) +{ + return x.id == y.id && x.str == y.str; +} + +#pragma db namespace schema(DB_SCHEMA) +namespace ns // Extension. +{ + #pragma db object + struct object2 + { + object2 (): obj1 (0) {} + ~object2 () {delete obj1;} + + #pragma db id + std::string id; + + std::vector<unsigned int> nums; + object1* obj1; + }; + + inline bool + operator== (const object2& x, const object2& y) + { + return x.id == y.id && x.nums == y.nums && *x.obj1 == *y.obj1; + } +} + +#pragma db view object(object1) object(ns::object2) +struct object_view +{ + #pragma db column(ns::object2::id) + std::string id2; + + std::string str; +}; + +#pragma db view table(DB_SCHEMA."schema_ns_object_1") +struct table_view +{ + #pragma db column(DB_SCHEMA."schema_ns_object_1"."str") + std::string str; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/schema/namespace/testscript b/odb-tests/common/schema/namespace/testscript new file mode 100644 index 0000000..a2edd8d --- /dev/null +++ b/odb-tests/common/schema/namespace/testscript @@ -0,0 +1,53 @@ +# file : common/schema/namespace/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/section/basics/buildfile b/odb-tests/common/section/basics/buildfile new file mode 100644 index 0000000..5ccdd6b --- /dev/null +++ b/odb-tests/common/section/basics/buildfile @@ -0,0 +1,41 @@ +# file : common/section/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_section_b_ \ + --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/section/basics/driver.cxx b/odb-tests/common/section/basics/driver.cxx new file mode 100644 index 0000000..53783a3 --- /dev/null +++ b/odb-tests/common/section/basics/driver.cxx @@ -0,0 +1,1735 @@ +// file : common/section/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test object section basics. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_ptr<object> p (db->load<object> (o.id)); + + db->update (o1, o1.s); + assert (o.v != o1.v); + + // Double-check object version was updated. + // + { + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_ptr<object> p (db->load<object> (o.id)); + + db->update (o1, o1.s); + assert (o.v != o1.v); + + // Double-check object version was updated. + // + { + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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 ()); + unique_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; + } +} diff --git a/odb-tests/common/section/basics/test.hxx b/odb-tests/common/section/basics/test.hxx new file mode 100644 index 0000000..702ef8b --- /dev/null +++ b/odb-tests/common/section/basics/test.hxx @@ -0,0 +1,628 @@ +// file : common/section/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> +#include <odb/section.hxx> +#include <odb/vector.hxx> + +#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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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<int> 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 (unsigned long id_ = 0, int n_ = 999, const std::string& s_ = "xxx") + : id (id_), sb (s_.begin (), s_.end ()), sn (n_), n (n_) {} + + #pragma db id + unsigned long id; + + #pragma db load(lazy) + odb::section s; + + #pragma db section(s) type(BLOB_TYPE) + std::vector<char> 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<int> 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<int> 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<int> 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<int> sv; + }; +} + +// Regression: BLOB in a value type used as a map value that is in a section. +// +#include <map> +#include <vector> + +#pragma db namespace table("t19_") +namespace test19 +{ + #pragma db value + struct value + { + #pragma db type(BLOB_TYPE) + std::vector<char> b; + }; + + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) update(always) + odb::section s; + + #pragma db section(s) + std::map<int, value> m; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/section/basics/testscript b/odb-tests/common/section/basics/testscript new file mode 100644 index 0000000..bf7d1db --- /dev/null +++ b/odb-tests/common/section/basics/testscript @@ -0,0 +1,53 @@ +# file : common/section/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/section/polymorphism/buildfile b/odb-tests/common/section/polymorphism/buildfile new file mode 100644 index 0000000..b9a7514 --- /dev/null +++ b/odb-tests/common/section/polymorphism/buildfile @@ -0,0 +1,41 @@ +# file : common/section/polymorphism/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_section_p_ \ + --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/section/polymorphism/driver.cxx b/odb-tests/common/section/polymorphism/driver.cxx new file mode 100644 index 0000000..bdde74f --- /dev/null +++ b/odb-tests/common/section/polymorphism/driver.cxx @@ -0,0 +1,1807 @@ +// file : common/section/polymorphism/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test sections in polymorphic objects. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic polymorphic section functionality. + // + { + using namespace test1; + + base b (123, "abc"); + derived d (234, "bcd", true); + + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + t.commit (); + + assert (b.rs1.loaded ()); + assert (b.rs2.loaded ()); + assert (b.rs3.loaded ()); + assert (b.rs4.loaded ()); + assert (b.bs1.loaded ()); + + assert (d.rs1.loaded ()); + assert (d.rs2.loaded ()); + assert (d.rs3.loaded ()); + assert (d.rs4.loaded ()); + assert (d.bs1.loaded ()); + assert (d.ds1.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pb->rs1.loaded ()); + assert (!pb->rs2.loaded ()); + assert (!pb->rs3.loaded ()); + assert (!pb->rs4.loaded ()); + assert (!pb->bs1.loaded ()); + + assert (!pd->rs1.loaded ()); + assert (!pd->rs2.loaded ()); + assert (!pd->rs3.loaded ()); + assert (!pd->rs4.loaded ()); + assert (!pd->bs1.loaded ()); + assert (!pd->ds1.loaded ()); + + assert (pb->rs1n != b.rs1n && pb->rs1s != b.rs1s && + pb->rs2n != b.rs2n && pb->rs2v != b.rs2v && + pb->rs3v != b.rs3v && + pb->rs4n != b.rs4n && + pb->bs1n != b.bs1n); + + assert (pd->rs1n != d.rs1n && pd->rs1s != d.rs1s && + pd->rs1b != d.rs1b && pd->rs1v != d.rs1v && + pd->rs2n != d.rs2n && pd->rs2v != d.rs2v && + pd->rs3v != d.rs3v && pd->rs3n != d.rs3n && + pd->rs4n != d.rs4n && pd->rs4s != d.rs4s && + pd->bs1n != d.bs1n && pd->bs1s != d.bs1s && + pd->ds1n != d.ds1n); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); + + root* pr (pd.get ()); + db->load (*pr, pr->rs1); // Via base. + db->load (*pd, pd->rs2); + db->load (*pr, pr->rs3); // Via base. + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + try + { + db->load (*pr, pd->bs1); // Object-section association is static. + assert (false); + } + catch (const section_not_in_object&) {} + + assert (pb->rs1.loaded ()); + assert (pb->rs2.loaded ()); + assert (pb->rs3.loaded ()); + assert (pb->rs4.loaded ()); + assert (pb->bs1.loaded ()); + + assert (pd->rs1.loaded ()); + assert (pd->rs2.loaded ()); + assert (pd->rs3.loaded ()); + assert (pd->rs4.loaded ()); + assert (pd->bs1.loaded ()); + assert (pd->ds1.loaded ()); + + assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s && + pb->rs2n == b.rs2n && pb->rs2v == b.rs2v && + pb->rs3v == b.rs3v && + pb->rs4n == b.rs4n && + pb->bs1n == b.bs1n); + + assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s && + pd->rs1b == d.rs1b && pd->rs1v == d.rs1v && + pd->rs2n == d.rs2n && pd->rs2v == d.rs2v && + pd->rs3v == d.rs3v && pd->rs3n == d.rs3n && + pd->rs4n == d.rs4n && pd->rs4s == d.rs4s && + pd->bs1n == d.bs1n && pd->bs1s == d.bs1s && + pd->ds1n == d.ds1n); + t.commit (); + } + + // Update object. + // + b.rs1n++; + b.rs1s += 'd'; + b.rs1.change (); + b.rs2n++; + b.rs2v[0]++; + b.rs3v[0]++; + b.rs4n++; + b.bs1n++; + + d.rs1n++; + d.rs1s += 'e'; + d.rs1b = !d.rs1b; + d.rs1v[0]++; + d.rs1.change (); + d.rs2n++; + d.rs2v[0]++; + d.rs3v[0]++; + d.rs3n++; + d.rs4n++; + d.rs4s += 'e'; + d.bs1n++; + d.bs1s += 'e'; + d.ds1n++; + + { + transaction t (db->begin ()); + db->update (b); + db->update (d); + t.commit (); + + assert (!b.rs1.changed ()); + assert (!d.rs1.changed ()); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); + + db->load (*pd, pd->rs1); + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s && + pb->rs2n == b.rs2n && pb->rs2v == b.rs2v && + pb->rs3v == b.rs3v && + pb->rs4n == b.rs4n && + pb->bs1n == b.bs1n); + + assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s && + pd->rs1b == d.rs1b && pd->rs1v == d.rs1v && + pd->rs2n == d.rs2n && pd->rs2v == d.rs2v && + pd->rs3v == d.rs3v && pd->rs3n == d.rs3n && + pd->rs4n == d.rs4n && pd->rs4s == d.rs4s && + pd->bs1n == d.bs1n && pd->bs1s == d.bs1s && + pd->ds1n == d.ds1n); + t.commit (); + } + + // Update section. + // + b.rs1n++; + b.rs1s += 'd'; + b.rs2n++; + b.rs2v[0]++; + b.rs3v[0]++; + b.rs4n++; + b.bs1n++; + + d.rs1n++; + d.rs1s += 'e'; + d.rs1b = !d.rs1b; + d.rs1v[0]++; + d.rs2n++; + d.rs2v[0]++; + d.rs3v[0]++; + d.rs3n++; + d.rs4n++; + d.rs4s += 'e'; + d.bs1n++; + d.bs1s += 'e'; + d.ds1n++; + + { + transaction t (db->begin ()); + db->update (b, b.rs1); + db->update (b, b.rs2); + db->update (b, b.rs3); + db->update (b, b.rs4); + db->update (b, b.bs1); + + db->update (d, d.rs1); + db->update (d, d.rs2); + db->update (d, d.rs3); + db->update (d, d.rs4); + db->update (d, d.bs1); + db->update (d, d.ds1); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); + + db->load (*pd, pd->rs1); + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s && + pb->rs2n == b.rs2n && pb->rs2v == b.rs2v && + pb->rs3v == b.rs3v && + pb->rs4n == b.rs4n && + pb->bs1n == b.bs1n); + + assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s && + pd->rs1b == d.rs1b && pd->rs1v == d.rs1v && + pd->rs2n == d.rs2n && pd->rs2v == d.rs2v && + pd->rs3v == d.rs3v && pd->rs3n == d.rs3n && + pd->rs4n == d.rs4n && pd->rs4s == d.rs4s && + pd->bs1n == d.bs1n && pd->bs1s == d.bs1s && + pd->ds1n == d.ds1n); + t.commit (); + } + + // Reload. + // + b.rs1n++; + b.rs1s += 'd'; + b.rs1.change (); + b.rs2n++; + b.rs2v[0]++; + b.rs3v[0]++; + b.rs4n++; + b.bs1n++; + + d.rs1n++; + d.rs1s += 'e'; + d.rs1b = !d.rs1b; + d.rs1v[0]++; + d.rs1.change (); + d.rs2n++; + d.rs2v[0]++; + d.rs3v[0]++; + d.rs3n++; + d.rs4n++; + d.rs4s += 'e'; + d.bs1n++; + d.bs1s += 'e'; + d.ds1n++; + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); + + db->load (*pd, pd->rs1); + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + db->update (b); + db->update (d); + + db->reload (*pb); + db->reload (*pd); + + assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s && + pb->rs2n == b.rs2n && pb->rs2v == b.rs2v && + pb->rs3v == b.rs3v && + pb->rs4n == b.rs4n && + pb->bs1n == b.bs1n); + + assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s && + pd->rs1b == d.rs1b && pd->rs1v == d.rs1v && + pd->rs2n == d.rs2n && pd->rs2v == d.rs2v && + pd->rs3v == d.rs3v && pd->rs3n == d.rs3n && + pd->rs4n == d.rs4n && pd->rs4s == d.rs4s && + pd->bs1n == d.bs1n && pd->bs1s == d.bs1s && + pd->ds1n == d.ds1n); + + t.commit (); + } + } + + // Test empty section and override "gap". + // + { + using namespace test2; + + derived d (234); + + { + transaction t (db->begin ()); + db->persist (d); + t.commit (); + + assert (d.s.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pd->s.loaded ()); + assert (pd->sn != d.sn && pd->sv != d.sv); + + root* pr (pd.get ()); + db->load (*pr, pr->s); // Via root. + + assert (pd->s.loaded ()); + assert (pd->sn == d.sn && pd->sv == d.sv); + t.commit (); + } + + // Update object. + // + d.sn++; + d.sv[0]++; + + { + transaction t (db->begin ()); + root* pr (&d); + db->update (pr); // Via root. + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + base* pb (pd.get ()); + db->load (*pb, pb->s); // Via base. + + assert (pd->sn == d.sn && pd->sv == d.sv); + t.commit (); + } + + // Update section. + // + d.sn++; + d.sv[0]++; + + { + transaction t (db->begin ()); + root* pr (&d); + db->update (*pr, pr->s); // Via root. + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pd, pd->s); + + assert (pd->sn == d.sn && pd->sv == d.sv); + t.commit (); + } + + // Reload. + // + d.sn++; + d.sv[0]++; + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pd, pd->s); + + db->update (d); + + root* pr (pd.get ()); + db->reload (*pr); + + assert (pd->sn == d.sn && pd->sv == d.sv); + t.commit (); + } + } + + // Test value-only/container-only base/override combinations. + // + { + using namespace test3; + + root r (123); + base b (234); + derived d (345, "abc"); + + { + transaction t (db->begin ()); + db->persist (r); + db->persist (b); + db->persist (d); + t.commit (); + + assert (r.s1.loaded ()); + assert (r.s2.loaded ()); + assert (r.s3.loaded ()); + assert (r.s4.loaded ()); + + assert (b.s1.loaded ()); + assert (b.s2.loaded ()); + assert (b.s3.loaded ()); + assert (b.s4.loaded ()); + + assert (d.s1.loaded ()); + assert (d.s2.loaded ()); + assert (d.s3.loaded ()); + assert (d.s4.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pr->s1.loaded ()); + assert (!pr->s2.loaded ()); + assert (!pr->s3.loaded ()); + assert (!pr->s4.loaded ()); + + assert (!pb->s1.loaded ()); + assert (!pb->s2.loaded ()); + assert (!pb->s3.loaded ()); + assert (!pb->s4.loaded ()); + + assert (!pd->s1.loaded ()); + assert (!pd->s2.loaded ()); + assert (!pd->s3.loaded ()); + assert (!pd->s4.loaded ()); + + assert (pr->s1n != r.s1n && + pr->s2n != r.s2n && + pr->s3v != r.s3v && + pr->s4nv != r.s4nv); + + assert (pb->s1n != b.s1n && + pb->s2n != b.s2n && + pb->s3v != b.s3v && + pb->s4nv != b.s4nv); + + assert (pd->s1n != d.s1n && pd->s1s != d.s1s && + pd->s2n != d.s2n && pd->s2v != d.s2v && + pd->s3v != d.s3v && pd->s3n != d.s3n && + pd->s4nv != d.s4nv && pd->s4sv != d.s4sv); + + db->load (*pr, pr->s1); + db->load (*pr, pr->s2); + db->load (*pr, pr->s3); + db->load (*pr, pr->s4); + + db->load (*pb, pb->s1); + db->load (*pb, pb->s2); + db->load (*pb, pb->s3); + db->load (*pb, pb->s4); + + root* pdr (pd.get ()); + db->load (*pdr, pdr->s1); + db->load (*pdr, pdr->s2); + db->load (*pdr, pdr->s3); + db->load (*pdr, pdr->s4); + + assert (pr->s1.loaded ()); + assert (pr->s2.loaded ()); + assert (pr->s3.loaded ()); + assert (pr->s4.loaded ()); + + assert (pb->s1.loaded ()); + assert (pb->s2.loaded ()); + assert (pb->s3.loaded ()); + assert (pb->s4.loaded ()); + + assert (pd->s1.loaded ()); + assert (pd->s2.loaded ()); + assert (pd->s3.loaded ()); + assert (pd->s4.loaded ()); + + assert (pr->s1n == r.s1n && + pr->s2n == r.s2n && + pr->s3v == r.s3v && + pr->s4nv == r.s4nv); + + assert (pb->s1n == b.s1n && + pb->s2n == b.s2n && + pb->s3v == b.s3v && + pb->s4nv == b.s4nv); + + assert (pd->s1n == d.s1n && pd->s1s == d.s1s && + pd->s2n == d.s2n && pd->s2v == d.s2v && + pd->s3v == d.s3v && pd->s3n == d.s3n && + pd->s4nv == d.s4nv && pd->s4sv == d.s4sv); + t.commit (); + } + + // Update object. + // + r.s1n++; + r.s2n++; + r.s3v[0]++; + r.s4nv[0]++; + + b.s1n++; + b.s2n++; + b.s3v[0]++; + b.s4nv[0]++; + + d.s1n++; + d.s1s += 'd'; + d.s2n++; + d.s2v[0]++; + d.s3v[0]++; + d.s3n++; + d.s4nv[0]++; + d.s4sv[0] += 'd'; + + { + transaction t (db->begin ()); + db->update (r); + db->update (b); + db->update (d); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pr, pr->s1); + db->load (*pr, pr->s2); + db->load (*pr, pr->s3); + db->load (*pr, pr->s4); + + db->load (*pb, pb->s1); + db->load (*pb, pb->s2); + db->load (*pb, pb->s3); + db->load (*pb, pb->s4); + + db->load (*pd, pd->s1); + db->load (*pd, pd->s2); + db->load (*pd, pd->s3); + db->load (*pd, pd->s4); + + assert (pr->s1n == r.s1n && + pr->s2n == r.s2n && + pr->s3v == r.s3v && + pr->s4nv == r.s4nv); + + assert (pb->s1n == b.s1n && + pb->s2n == b.s2n && + pb->s3v == b.s3v && + pb->s4nv == b.s4nv); + + assert (pd->s1n == d.s1n && pd->s1s == d.s1s && + pd->s2n == d.s2n && pd->s2v == d.s2v && + pd->s3v == d.s3v && pd->s3n == d.s3n && + pd->s4nv == d.s4nv && pd->s4sv == d.s4sv); + t.commit (); + } + + // Update section. + // + r.s1n++; + r.s2n++; + r.s3v[0]++; + r.s4nv[0]++; + + b.s1n++; + b.s2n++; + b.s3v[0]++; + b.s4nv[0]++; + + d.s1n++; + d.s1s += 'd'; + d.s2n++; + d.s2v[0]++; + d.s3v[0]++; + d.s3n++; + d.s4nv[0]++; + d.s4sv[0] += 'd'; + + { + transaction t (db->begin ()); + db->update (r, r.s1); + db->update (r, r.s2); + db->update (r, r.s3); + db->update (r, r.s4); + + db->update (b, b.s1); + db->update (b, b.s2); + db->update (b, b.s3); + db->update (b, b.s4); + + root& rr (d); + db->update (rr, rr.s1); + db->update (rr, rr.s2); + db->update (rr, rr.s3); + db->update (rr, rr.s4); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pr, pr->s1); + db->load (*pr, pr->s2); + db->load (*pr, pr->s3); + db->load (*pr, pr->s4); + + db->load (*pb, pb->s1); + db->load (*pb, pb->s2); + db->load (*pb, pb->s3); + db->load (*pb, pb->s4); + + db->load (*pd, pd->s1); + db->load (*pd, pd->s2); + db->load (*pd, pd->s3); + db->load (*pd, pd->s4); + + assert (pr->s1n == r.s1n && + pr->s2n == r.s2n && + pr->s3v == r.s3v && + pr->s4nv == r.s4nv); + + assert (pb->s1n == b.s1n && + pb->s2n == b.s2n && + pb->s3v == b.s3v && + pb->s4nv == b.s4nv); + + assert (pd->s1n == d.s1n && pd->s1s == d.s1s && + pd->s2n == d.s2n && pd->s2v == d.s2v && + pd->s3v == d.s3v && pd->s3n == d.s3n && + pd->s4nv == d.s4nv && pd->s4sv == d.s4sv); + t.commit (); + } + + // Reload. + // + r.s1n++; + r.s2n++; + r.s3v[0]++; + r.s4nv[0]++; + + b.s1n++; + b.s2n++; + b.s3v[0]++; + b.s4nv[0]++; + + d.s1n++; + d.s1s += 'd'; + d.s2n++; + d.s2v[0]++; + d.s3v[0]++; + d.s3n++; + d.s4nv[0]++; + d.s4sv[0] += 'd'; + + { + transaction t (db->begin ()); + unique_ptr<root> pr (db->load<root> (r.id)); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->load (*pr, pr->s1); + db->load (*pr, pr->s2); + db->load (*pr, pr->s3); + db->load (*pr, pr->s4); + + db->load (*pb, pb->s1); + db->load (*pb, pb->s2); + db->load (*pb, pb->s3); + db->load (*pb, pb->s4); + + db->load (*pd, pd->s1); + db->load (*pd, pd->s2); + db->load (*pd, pd->s3); + db->load (*pd, pd->s4); + + db->update (r); + db->update (b); + db->update (d); + + db->reload (*pr); + db->reload (*pb); + db->reload (*pd); + + assert (pr->s1n == r.s1n && + pr->s2n == r.s2n && + pr->s3v == r.s3v && + pr->s4nv == r.s4nv); + + assert (pb->s1n == b.s1n && + pb->s2n == b.s2n && + pb->s3v == b.s3v && + pb->s4nv == b.s4nv); + + assert (pd->s1n == d.s1n && pd->s1s == d.s1s && + pd->s2n == d.s2n && pd->s2v == d.s2v && + pd->s3v == d.s3v && pd->s3n == d.s3n && + pd->s4nv == d.s4nv && pd->s4sv == d.s4sv); + t.commit (); + } + } + + // Test basic polymorphic optimistic section functionality. + // + { + using namespace test4; + + base b (123, "abc"); + derived d (234, "bcd", true); + + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + t.commit (); + + assert (b.rs1.loaded ()); + assert (b.rs2.loaded ()); + assert (b.rs3.loaded ()); + assert (b.rs4.loaded ()); + assert (b.bs1.loaded ()); + + assert (d.rs1.loaded ()); + assert (d.rs2.loaded ()); + assert (d.rs3.loaded ()); + assert (d.rs4.loaded ()); + assert (d.bs1.loaded ()); + assert (d.ds1.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pb->rs1.loaded ()); + assert (!pb->rs2.loaded ()); + assert (!pb->rs3.loaded ()); + assert (!pb->rs4.loaded ()); + assert (!pb->bs1.loaded ()); + + assert (!pd->rs1.loaded ()); + assert (!pd->rs2.loaded ()); + assert (!pd->rs3.loaded ()); + assert (!pd->rs4.loaded ()); + assert (!pd->bs1.loaded ()); + assert (!pd->ds1.loaded ()); + + assert (pb->rs1n != b.rs1n && pb->rs1s != b.rs1s && + pb->rs2n != b.rs2n && + pb->rs3n != b.rs3n && + pb->rs4n != b.rs4n && pb->rs4s != b.rs4s); + + assert (pd->rs1n != d.rs1n && pd->rs1s != d.rs1s && + pd->rs1b != d.rs1b && pd->rs1v != d.rs1v && + pd->rs2n != d.rs2n && + pd->rs3n != d.rs3n && pd->rs3s != d.rs3s && + pd->rs4n != d.rs4n && pd->rs4s != d.rs4s && + pd->rs4v != d.rs4v && + pd->bs1n != d.bs1n && + pd->ds1v != d.ds1v); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); // No-op. + + root* pr (pd.get ()); + db->load (*pr, pr->rs1); // Via base. + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pr, pr->rs4); // Via base. + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + assert (pb->rs1.loaded ()); + assert (pb->rs2.loaded ()); + assert (pb->rs3.loaded ()); + assert (pb->rs4.loaded ()); + assert (pb->bs1.loaded ()); + + assert (pd->rs1.loaded ()); + assert (pd->rs2.loaded ()); + assert (pd->rs3.loaded ()); + assert (pd->rs4.loaded ()); + assert (pd->bs1.loaded ()); + assert (pd->ds1.loaded ()); + + assert (pb->rs1n == b.rs1n && pb->rs1s == b.rs1s && + pb->rs2n == b.rs2n && + pb->rs3n == b.rs3n && + pb->rs4n == b.rs4n && pb->rs4s == b.rs4s); + + assert (pd->rs1n == d.rs1n && pd->rs1s == d.rs1s && + pd->rs1b == d.rs1b && pd->rs1v == d.rs1v && + pd->rs2n == d.rs2n && + pd->rs3n == d.rs3n && pd->rs3s == d.rs3s && + pd->rs4n == d.rs4n && pd->rs4s == d.rs4s && + pd->rs4v == d.rs4v && + pd->bs1n == d.bs1n && + pd->ds1v == d.ds1v); + t.commit (); + } + + base b1 (b); + derived d1 (d); + + // Update object. + // + for (unsigned short s (1); s < 7; ++s) + { + b1.rs1n++; + b1.rs1s += 'd'; + b1.rs1.change (); + b1.rs4s += 'd'; + + d1.rs1n++; + d1.rs1s += 'e'; + d1.rs1b = !d.rs1b; + d1.rs1v[0]++; + d1.rs1.change (); + d1.rs4s += 'e'; + d1.rs4v[0]++; + d1.bs1n++; + d1.ds1v[0]++; + + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (b1); + db->update (d1); + + assert (!b1.rs1.changed ()); + assert (!d1.rs1.changed ()); + + assert (b.v != b1.v); + assert (d.v != d1.v); + + try + { + bool a (false); + switch (s) + { + case 1: db->load (*pb, pb->rs1); break; + case 2: db->load (*pb, pb->rs2); break; + case 3: db->load (*pb, pb->rs3); break; + case 4: db->load (*pb, pb->rs4); break; + case 5: + case 6: a = true; break; // No-op. + default: break; + } + assert (a); + } + catch (const object_changed&) + { + db->reload (*pb); + + assert (!pb->rs1.loaded ()); + assert (!pb->rs2.loaded ()); + assert (!pb->rs3.loaded ()); + assert (!pb->rs3.loaded ()); + assert (!pb->bs1.loaded ()); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); // No-op. + + assert (pb->rs1n == b1.rs1n && pb->rs1s == b1.rs1s && + pb->rs2n == b1.rs2n && + pb->rs3n == b1.rs3n && + pb->rs4n == b1.rs4n && pb->rs4s == b1.rs4s); + } + + try + { + switch (s) + { + case 1: db->load (*pd, pd->rs1); break; + case 2: db->load (*pd, pd->rs2); break; + case 3: db->load (*pd, pd->rs3); break; + case 4: db->load (*pd, pd->rs4); break; + case 5: db->load (*pd, pd->bs1); break; + case 6: db->load (*pd, pd->ds1); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + + assert (!pd->rs1.loaded ()); + assert (!pd->rs2.loaded ()); + assert (!pd->rs3.loaded ()); + assert (!pd->rs4.loaded ()); + assert (!pd->bs1.loaded ()); + assert (!pd->ds1.loaded ()); + + db->load (*pd, pd->rs1); + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + assert (pd->rs1n == d1.rs1n && pd->rs1s == d1.rs1s && + pd->rs1b == d1.rs1b && pd->rs1v == d1.rs1v && + pd->rs2n == d1.rs2n && + pd->rs3n == d1.rs3n && pd->rs3s == d1.rs3s && + pd->rs4n == d1.rs4n && pd->rs4s == d1.rs4s && + pd->rs4v == d1.rs4v && + pd->bs1n == d1.bs1n && + pd->ds1v == d1.ds1v); + } + + db->reload (b); + db->reload (d); + + assert (b.v == b1.v); + assert (d.v == d1.v); + + assert (b.rs1n == b1.rs1n && b.rs1s == b1.rs1s && + b.rs2n == b1.rs2n && + b.rs3n == b1.rs3n && + b.rs4n == b1.rs4n && b.rs4s == b1.rs4s); + + assert (d.rs1n == d1.rs1n && d.rs1s == d1.rs1s && + d.rs1b == d1.rs1b && d.rs1v == d1.rs1v && + d.rs2n == d1.rs2n && + d.rs3n == d1.rs3n && d.rs3s == d1.rs3s && + d.rs4n == d1.rs4n && d.rs4s == d1.rs4s && + d.rs4v == d1.rs4v && + d.bs1n == d1.bs1n && + d.ds1v == d1.ds1v); + + t.commit (); + } + + // Update section. + // + for (unsigned short s (1); s < 7; ++s) + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + switch (s) + { + case 1: + b1.rs1n++; + b1.rs1s += 'd'; + + d1.rs1n++; + d1.rs1s += 'e'; + d1.rs1b = !d.rs1b; + d1.rs1v[0]++; + + db->update (b1, b1.rs1); + db->update (d1, d1.rs1); + + assert (b.v != b1.v); + assert (d.v != d1.v); + break; + case 2: + db->update (b1, b1.rs2); // No-op. + db->update (d1, d1.rs2); // No-op. + + assert (b.v == b1.v); + assert (d.v == d1.v); + continue; // Object hasn't changed. + case 3: + db->update (b1, b1.rs3); // No-op. + db->update (d1, d1.rs3); // No-op. + + assert (b.v == b1.v); + assert (d.v == d1.v); + continue; // Object hasn't changed. + case 4: + b1.rs4s += 'd'; + + d1.rs4s += 'e'; + d1.rs4v[0]++; + + db->update (b1, b1.rs4); + db->update (d1, d1.rs4); + + assert (b.v != b1.v); + assert (d.v != d1.v); + break; + case 5: + d1.bs1n++; + + db->update (b1, b1.bs1); // No-op. + db->update (d1, d1.bs1); + + assert (b.v == b1.v); + assert (d.v != d1.v); + break; + case 6: + d1.ds1v[0]++; + + db->update (d1, d1.ds1); + + assert (d.v != d1.v); + break; + default: break; + } + + try + { + bool a (false); + switch (s) + { + case 1: db->load (*pb, pb->rs1); break; + case 4: db->load (*pb, pb->rs4); break; + case 5: + case 6: a = true; break; // No-op. + default: break; + } + assert (a); + } + catch (const object_changed&) + { + db->reload (*pb); + + assert (!pb->rs1.loaded ()); + assert (!pb->rs2.loaded ()); + assert (!pb->rs3.loaded ()); + assert (!pb->rs4.loaded ()); + assert (!pb->bs1.loaded ()); + + db->load (*pb, pb->rs1); + db->load (*pb, pb->rs2); + db->load (*pb, pb->rs3); + db->load (*pb, pb->rs4); + db->load (*pb, pb->bs1); // No-op. + + assert (pb->rs1n == b1.rs1n && pb->rs1s == b1.rs1s && + pb->rs2n == b1.rs2n && + pb->rs3n == b1.rs3n && + pb->rs4n == b1.rs4n && pb->rs4s == b1.rs4s); + } + + try + { + switch (s) + { + case 1: db->load (*pd, pd->rs1); break; + case 4: db->load (*pd, pd->rs4); break; + case 5: db->load (*pd, pd->bs1); break; + case 6: db->load (*pd, pd->ds1); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + + assert (!pd->rs1.loaded ()); + assert (!pd->rs2.loaded ()); + assert (!pd->rs3.loaded ()); + assert (!pd->rs4.loaded ()); + assert (!pd->bs1.loaded ()); + assert (!pd->ds1.loaded ()); + + db->load (*pd, pd->rs1); + db->load (*pd, pd->rs2); + db->load (*pd, pd->rs3); + db->load (*pd, pd->rs4); + db->load (*pd, pd->bs1); + db->load (*pd, pd->ds1); + + assert (pd->rs1n == d1.rs1n && pd->rs1s == d1.rs1s && + pd->rs1b == d1.rs1b && pd->rs1v == d1.rs1v && + pd->rs2n == d1.rs2n && + pd->rs3n == d1.rs3n && pd->rs3s == d1.rs3s && + pd->rs4n == d1.rs4n && pd->rs4s == d1.rs4s && + pd->rs4v == d1.rs4v && + pd->bs1n == d1.bs1n && + pd->ds1v == d1.ds1v); + } + + db->reload (b); + db->reload (d); + + assert (b.v == b1.v); + assert (d.v == d1.v); + + assert (b.rs1n == b1.rs1n && b.rs1s == b1.rs1s && + b.rs2n == b1.rs2n && + b.rs3n == b1.rs3n && + b.rs4n == b1.rs4n && b.rs4s == b1.rs4s); + + assert (d.rs1n == d1.rs1n && d.rs1s == d1.rs1s && + d.rs1b == d1.rs1b && d.rs1v == d1.rs1v && + d.rs2n == d1.rs2n && + d.rs3n == d1.rs3n && d.rs3s == d1.rs3s && + d.rs4n == d1.rs4n && d.rs4s == d1.rs4s && + d.rs4v == d1.rs4v && + d.bs1n == d1.bs1n && + d.ds1v == d1.ds1v); + + t.commit (); + } + + // Update changed section. + // + for (unsigned short s (1); s < 7; ++s) + { + if (s == 2 || s == 3) // Readonly sections. + continue; + + transaction t (db->begin ()); + + switch (s) + { + case 1: + b1.rs1n++; + b1.rs1s += 'd'; + + d1.rs1n++; + d1.rs1s += 'e'; + d1.rs1b = !d.rs1b; + d1.rs1v[0]++; + + db->update (b1, b1.rs1); + db->update (d1, d1.rs1); + break; + case 4: + b1.rs4s += 'd'; + + d1.rs4s += 'e'; + d1.rs4v[0]++; + + db->update (b1, b1.rs4); + db->update (d1, d1.rs4); + break; + case 5: + d1.bs1n++; + + db->update (b1, b1.bs1); // No-op. + db->update (d1, d1.bs1); + break; + case 6: + d1.ds1v[0]++; + + db->update (d1, d1.bs1); + break; + default: break; + } + + try + { + bool a (false); + switch (s) + { + case 1: db->update (b, b.rs1); break; + case 4: db->update (b, b.rs4); break; + case 5: + case 6: a = true; break; // No-op. + default: break; + } + assert (a); + } + catch (const object_changed&) + { + db->reload (b); + + switch (s) + { + case 1: db->update (b, b.rs1); break; + case 4: db->update (b, b.rs4); break; + default: break; + } + } + + try + { + switch (s) + { + case 1: db->update (d, d.rs1); break; + case 4: db->update (d, d.rs4); break; + case 5: db->update (d, d.bs1); break; + case 6: db->update (d, d.ds1); break; + default: break; + } + assert (false); + } + catch (const object_changed&) + { + db->reload (d); + + switch (s) + { + case 1: db->update (d, d.rs1); break; + case 4: db->update (d, d.rs4); break; + case 5: db->update (d, d.bs1); break; + case 6: db->update (d, d.ds1); break; + default: break; + } + } + + db->reload (b1); + db->reload (d1); + + t.commit (); + } + } + + // Test polymorphic optimistic readonly/empty to readwrite section + // override. + // + { + using namespace test5; + + base b; + derived d (123); + + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + t.commit (); + + assert (b.s.loaded ()); + assert (d.s.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pb->s.loaded ()); + assert (!pd->s.loaded ()); + assert (pd->sn != d.sn); + + db->load (*pb, pb->s); // No-op. + db->load (*pd, pd->s); + + assert (pb->s.loaded ()); + assert (pd->s.loaded ()); + + assert (pd->sn == d.sn); + + t.commit (); + } + + // Update object. + // + base b1 (b); + derived d1 (d); + d1.sn++; + d1.s.change (); + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (d1); + + assert (!d1.s.changed ()); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update section. + // + d1.sn++; + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (b1, b1.s); // No-op. + db->update (d1, d1.s); + + assert (b.v == b1.v); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update changed section. + // + d1.sn++; + + { + transaction t (db->begin ()); + db->update (d1, d1.s); + + try + { + db->update (d, d.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (d); + db->update (d, d.s); + } + + t.commit (); + } + } + + // Test polymorphic optimistic readonly/empty to readwrite section + // override, eager-loaded case. + // + { + using namespace test6; + + derived d (123); + + { + transaction t (db->begin ()); + db->persist (d); + t.commit (); + + assert (d.s.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (pd->s.loaded ()); + assert (pd->sn == d.sn); + + t.commit (); + } + + // Update object. + // + derived d1 (d); + d1.sn++; + d1.s.change (); + + { + transaction t (db->begin ()); + + db->update (d1); + + assert (!d1.s.changed ()); + assert (d.v != d1.v); + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update section. + // + d1.sn++; + + { + transaction t (db->begin ()); + + db->update (d1, d1.s); + assert (d.v != d1.v); + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update changed section. + // + d1.sn++; + + { + transaction t (db->begin ()); + db->update (d1, d1.s); + + try + { + db->update (d, d.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (d); + db->update (d, d.s); + } + + t.commit (); + } + } + + // Test polymorphic optimistic section added in derived. + // + { + using namespace test7; + + base b; + derived d (123); + + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d); + t.commit (); + + assert (b.s.loaded ()); + assert (d.s.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<base> pb (db->load<base> (b.id)); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pb->s.loaded ()); + assert (!pd->s.loaded ()); + assert (pd->sn != d.sn); + + db->load (*pb, pb->s); // No-op. + db->load (*pd, pd->s); + + assert (pb->s.loaded ()); + assert (pd->s.loaded ()); + + assert (pd->sn == d.sn); + + t.commit (); + } + + // Update object. + // + base b1 (b); + derived d1 (d); + d1.sn++; + d1.s.change (); + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (d1); + + assert (!d1.s.changed ()); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update section. + // + d1.sn++; + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (b1, b1.s); // No-op. + db->update (d1, d1.s); + + assert (b.v == b1.v); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update changed section. + // + d1.sn++; + + { + transaction t (db->begin ()); + db->update (d1, d1.s); + + try + { + db->update (d, d.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (d); + db->update (d, d.s); + } + + t.commit (); + } + } + + // Test reuse/polymorphic inheritance and optimistic mix. + // + { + using namespace test8; + + derived d (123); + + { + transaction t (db->begin ()); + db->persist (d); + t.commit (); + + assert (d.s.loaded ()); + } + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + assert (!pd->s.loaded ()); + assert (pd->sn != d.sn); + + db->load (*pd, pd->s); + + assert (pd->s.loaded ()); + assert (pd->sn == d.sn); + + t.commit (); + } + + // Update object. + // + derived d1 (d); + d1.sn++; + d1.s.change (); + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (d1); + + assert (!d1.s.changed ()); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update section. + // + d1.sn++; + + { + transaction t (db->begin ()); + unique_ptr<derived> pd (db->load<derived> (d.id)); + + db->update (d1, d1.s); + assert (d.v != d1.v); + + try + { + db->load (*pd, pd->s); + assert (false); + } + catch (const object_changed&) + { + db->reload (*pd); + assert (!pd->s.loaded ()); + db->load (*pd, pd->s); + + assert (pd->sn == d1.sn); + } + + db->reload (d); + assert (d.v == d1.v); + assert (d.sn == d1.sn); + t.commit (); + } + + // Update changed section. + // + d1.sn++; + + { + transaction t (db->begin ()); + db->update (d1, d1.s); + + try + { + db->update (d, d.s); + assert (false); + } + catch (const object_changed&) + { + db->reload (d); + db->update (d, d.s); + } + + t.commit (); + } + } + + // Test reuse/polymorphic inheritance and optimistic mix. + // + { + using namespace test9; + using std::shared_ptr; + + unsigned long id; + + { + container c (123); + + c.e1.push_back (shared_ptr<element> (new element (11))); + c.e1.push_back (shared_ptr<element> (new element (12))); + + c.e2.push_back (shared_ptr<element> (new element (21))); + c.e2.push_back (shared_ptr<element> (new element (22))); + + transaction t (db->begin ()); + + db->persist (c.e1[0]); + db->persist (c.e1[1]); + db->persist (c.e2[0]); + db->persist (c.e2[1]); + + id = db->persist (c); + + t.commit (); + } + + { + transaction t (db->begin ()); + + shared_ptr<container> c (db->load<container> (id)); + + assert (c->n == 123); + db->load (*c, c->s); + assert (c->e1.size () == 2 && c->e1[0]->n == 11 && c->e1[1]->n == 12); + assert (c->e2.size () == 2 && c->e2[0]->n == 21 && c->e2[1]->n == 22); + + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/section/polymorphism/test.hxx b/odb-tests/common/section/polymorphism/test.hxx new file mode 100644 index 0000000..6d524bd --- /dev/null +++ b/odb-tests/common/section/polymorphism/test.hxx @@ -0,0 +1,542 @@ +// file : common/section/polymorphism/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> +#include <memory> + +#include <odb/core.hxx> +#include <odb/section.hxx> + +// Test basic polymorphic section functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db object polymorphic abstract + struct root + { + root (int n): rs1n (n), rs2n (n), rs4n (n) {rs2v.push_back (n);} + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + // rs1: override in base and derived + // + #pragma db load(lazy) update(change) + odb::section rs1; + + #pragma db section(rs1) + int rs1n; + + // rs2: no override + // + #pragma db load(lazy) + odb::section rs2; + + #pragma db section(rs2) + int rs2n; + + #pragma db section(rs2) + std::vector<int> rs2v; + + // rs3: empty + // + #pragma db load(lazy) + odb::section rs3; + + // rs4: override "gap" + // + #pragma db load(lazy) + odb::section rs4; + + #pragma db section(rs4) + int rs4n; + }; + + #pragma db object + struct base: root + { + base (int n = 999, const std::string& s = "xxx") + : root (n), rs1s (s), bs1n (n) {rs3v.push_back (n);} + + // rs1 + // + #pragma db section(rs1) + std::string rs1s; + + // rs3 + // + #pragma db section(rs3) + std::vector<int> rs3v; + + // bs1: override in derived + // + #pragma db load(lazy) + odb::section bs1; + + #pragma db section(bs1) + int bs1n; + }; + + #pragma db object + struct derived: base + { + derived (int n = 999, const std::string& s = "xxx", bool b = false) + : base (n, s), rs1b (b), rs3n (n), rs4s (s), bs1s (s), ds1n (n) + {rs1v.push_back (n);} + + // rs1 + // + #pragma db section(rs1) + bool rs1b; + + #pragma db section(rs1) + std::vector<int> rs1v; + + // rs3 + // + #pragma db section(rs3) + int rs3n; + + // rs4 + // + #pragma db section(rs4) + std::string rs4s; + + // bs1 + // + #pragma db section(bs1) + std::string bs1s; + + // ds1: no override + // + #pragma db load(lazy) + odb::section ds1; + + #pragma db section(ds1) + int ds1n; + }; +} + +// Test empty section and override "gap". +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db object polymorphic abstract + struct root + { + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + #pragma db load(lazy) + odb::section s; + }; + + #pragma db object abstract + struct base: root + { + // The "gap". + }; + + #pragma db object + struct derived: base + { + derived (int n = 999): sn (n) {sv.push_back (n);} + + #pragma db section(s) + int sn; + + #pragma db section(s) + std::vector<int> sv; + }; +} + +// Test value-only/container-only base/override combinations. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db object polymorphic + struct root + { + root (int n = 999) + : s1n (n), s2n (n) {s3v.push_back (n); s4nv.push_back (n);} + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + // value/value + // + #pragma db load(lazy) + odb::section s1; + + #pragma db section(s1) + int s1n; + + // value/container + // + #pragma db load(lazy) + odb::section s2; + + #pragma db section(s2) + int s2n; + + // container/value + // + #pragma db load(lazy) + odb::section s3; + + #pragma db section(s3) + std::vector<int> s3v; + + // container/container + // + #pragma db load(lazy) + odb::section s4; + + #pragma db section(s4) + std::vector<int> s4nv; + }; + + #pragma db object + struct base: root + { + base (int n = 999): root (n) {} + + // The "gap". + }; + + #pragma db object + struct derived: base + { + derived (int n = 999, const std::string& s = "xxx") + : base (n), s1s (s), s3n (n) {s2v.push_back (n); s4sv.push_back (s);} + + #pragma db section(s1) + std::string s1s; + + #pragma db section(s2) + std::vector<int> s2v; + + #pragma db section(s3) + int s3n; + + #pragma db section(s4) + std::vector<std::string> s4sv; + }; +} + +// Test basic polymorphic optimistic section functionality. +// +#pragma db namespace table("t4_") +namespace test4 +{ + #pragma db object polymorphic optimistic abstract sectionable + struct root + { + root (int n): rs1n (n), rs2n (n), rs3n (n), rs4n (n) {} + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + #pragma db version + unsigned long long v; + + // rs1: readwrite, override + // + #pragma db load(lazy) update(change) + odb::section rs1; + + #pragma db section(rs1) + int rs1n; + + // rs2: readonly, no override + // + #pragma db load(lazy) + odb::section rs2; + + #pragma db section(rs2) + const int rs2n; + + // rs3: readonly, readonly override + // + #pragma db load(lazy) + odb::section rs3; + + #pragma db section(rs3) + const int rs3n; + + // rs4: readonly, readwrite override + // + #pragma db load(lazy) + odb::section rs4; + + #pragma db section(rs4) + const int rs4n; + }; + + #pragma db object + struct base: root + { + base (int n = 999, const std::string& s = "xxx") + : root (n), rs1s (s), rs4s (s) {} + + // rs1 + // + #pragma db section(rs1) + std::string rs1s; + + // rs4 + // + #pragma db section(rs4) + std::string rs4s; + + // bs2: empty, readwrite override + // + #pragma db load(lazy) + odb::section bs1; + }; + + #pragma db object + struct derived: base + { + derived (int n = 999, const std::string& s = "xxx", bool b = false) + : base (n, s), rs1b (b), rs3s (s), bs1n (n) + { + rs1v.push_back (n); + rs4v.push_back (n); + ds1v.push_back (n); + } + + // rs1 + // + #pragma db section(rs1) + bool rs1b; + + #pragma db section(rs1) + std::vector<int> rs1v; + + // rs3 + // + #pragma db section(rs3) + const std::string rs3s; + + // rs4 + // + #pragma db section(rs4) + std::vector<int> rs4v; + + // bs1 + // + #pragma db section(bs1) + int bs1n; + + // ds1: readwrite + // + #pragma db load(lazy) + odb::section ds1; + + #pragma db section(ds1) + std::vector<int> ds1v; + }; +} + +// Test polymorphic optimistic readonly/empty to readwrite section override. +// +#pragma db namespace table("t5_") +namespace test5 +{ + #pragma db object polymorphic optimistic abstract + struct root + { + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + #pragma db version + unsigned long long v; + + #pragma db load(lazy) update(change) + odb::section s; + }; + + #pragma db object + struct base: root + { + // The "gap". + }; + + #pragma db object + struct derived: base + { + derived (int n = 999): sn (n) {} + + #pragma db section(s) + int sn; + }; +} + +// Test polymorphic optimistic readonly/empty to readwrite section override, +// eager-loaded case. +// +#pragma db namespace table("t6_") +namespace test6 +{ + #pragma db object polymorphic optimistic abstract + struct root + { + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + #pragma db version + unsigned long long v; + + #pragma db update(change) + odb::section s; + }; + + #pragma db object abstract + struct base: root + { + // The "gap". + }; + + #pragma db object + struct derived: base + { + derived (int n = 999): sn (n) {} + + #pragma db section(s) + int sn; + }; +} + +// Test polymorphic optimistic section added in derived. +// +#pragma db namespace table("t7_") +namespace test7 +{ + #pragma db object polymorphic optimistic sectionable + struct root + { + virtual ~root () {} + + #pragma db id auto + unsigned long id; + + #pragma db version + unsigned long long v; + }; + + #pragma db object + struct base: root + { + #pragma db load(lazy) update(change) + odb::section s; + }; + + #pragma db object + struct derived: base + { + derived (int n = 999): sn (n) {} + + #pragma db section(s) + int sn; + }; +} + +// Test reuse/polymorphic inheritance and optimistic mix. +// +#pragma db namespace table("t8_") +namespace test8 +{ + #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 polymorphic sectionable + struct base: root + { + virtual ~base () {} + }; + + #pragma db object + struct derived: base + { + derived (int n = 999): sn (n) {} + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + int sn; + }; +} + +// Test id overwrite regression. +// +// The key here is the setup: the object that contains the containers in a +// section and the pointers to objects stored in those containers. And these +// objects derive polymorphically from the same base (and thus shared the id +// bindind). +// +#pragma db namespace table("t9_") +namespace test9 +{ + #pragma db object polymorphic pointer(std::shared_ptr) + struct base + { + virtual ~base () {} + + #pragma db id auto + unsigned long id; + }; + + #pragma db object + struct element: base + { + element (int n_ = 0): n (n_) {} + + int n; + }; + + typedef std::vector<std::shared_ptr<element>> elements; + + #pragma db object + struct container: base + { + container (int n_ = 0): n (n_) {} + + int n; + + #pragma db load(lazy) update(always) + odb::section s; + + #pragma db section(s) + elements e1; + + #pragma db section(s) + elements e2; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/section/polymorphism/testscript b/odb-tests/common/section/polymorphism/testscript new file mode 100644 index 0000000..9fd2310 --- /dev/null +++ b/odb-tests/common/section/polymorphism/testscript @@ -0,0 +1,53 @@ +# file : common/section/polymorphism/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/session/cache/buildfile b/odb-tests/common/session/cache/buildfile new file mode 100644 index 0000000..6d5b0bc --- /dev/null +++ b/odb-tests/common/session/cache/buildfile @@ -0,0 +1,41 @@ +# file : common/session/cache/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 session_cache_ \ + --generate-schema \ + --generate-session + +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/session/cache/driver.cxx b/odb-tests/common/session/cache/driver.cxx new file mode 100644 index 0000000..4b4ea12 --- /dev/null +++ b/odb-tests/common/session/cache/driver.cxx @@ -0,0 +1,83 @@ +// file : common/session/cache/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test session object cache. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test the session_required exception. + // + { + using namespace test1; + + shared_ptr<obj1> o1a (new obj1 (1)); + shared_ptr<obj1> o1b (new obj1 (2)); + shared_ptr<obj2> o2 (new obj2 (1)); + + o1a->o2 = o2; + o1b->o2 = o2; + + o2->o1.push_back (o1a); + o2->o1.push_back (o1b); + + { + transaction t (db->begin ()); + db->persist (o1a); + db->persist (o1b); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + + try + { + shared_ptr<obj1> o1 (db->load<obj1> (1)); + assert (false); + } + catch (const session_required&) + { + } + + t.commit (); + } + + { + session s; + transaction t (db->begin ()); + shared_ptr<obj1> o1 (db->load<obj1> (1)); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/session/cache/test.hxx b/odb-tests/common/session/cache/test.hxx new file mode 100644 index 0000000..d2b1b2b --- /dev/null +++ b/odb-tests/common/session/cache/test.hxx @@ -0,0 +1,50 @@ +// file : common/session/cache/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <memory> +#include <vector> + +#include <odb/core.hxx> + +// Test the session_required exception. +// +#pragma db namespace table("t1_") +namespace test1 +{ + using std::shared_ptr; + using std::weak_ptr; + + #pragma db namespace(test1) pointer(shared_ptr) + + struct obj2; + + #pragma db object + struct obj1 + { + obj1 () {} + obj1 (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + shared_ptr<obj2> o2; + }; + + #pragma db object + struct obj2 + { + obj2 () {} + obj2 (unsigned long id): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db inverse (o2) + std::vector< weak_ptr<obj1> > o1; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/session/cache/testscript b/odb-tests/common/session/cache/testscript new file mode 100644 index 0000000..da4e134 --- /dev/null +++ b/odb-tests/common/session/cache/testscript @@ -0,0 +1,53 @@ +# file : common/session/cache/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/session/custom/buildfile b/odb-tests/common/session/custom/buildfile new file mode 100644 index 0000000..1b64de1 --- /dev/null +++ b/odb-tests/common/session/custom/buildfile @@ -0,0 +1,43 @@ +# file : common/session/custom/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +exe{driver}: {hxx txx 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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 session_custom_ \ + --generate-schema \ + --generate-session \ + --session-type ::session \ + --hxx-prologue '#include "session.hxx"' + +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/session/custom/driver.cxx b/odb-tests/common/session/custom/driver.cxx new file mode 100644 index 0000000..3056fd6 --- /dev/null +++ b/odb-tests/common/session/custom/driver.cxx @@ -0,0 +1,231 @@ +// file : common/session/custom/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test custom session (C++11 only). +// + +#include <memory> +#include <cstddef> // std::size_t +#include <iostream> + +#include <odb/tracer.hxx> +#include <odb/database.hxx> +#include <odb/session.hxx> +#include <odb/transaction.hxx> +#include <odb/details/config.hxx> // ODB_CXX11_* + +#include <libcommon/common.hxx> + +#include "session.hxx" + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; + +using odb::database; +using odb::transaction; + +struct counting_tracer: odb::tracer +{ + virtual void + execute (odb::connection&, const char*) {count++;} + size_t count; +}; + +static counting_tracer tracer; + +struct failed {}; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Simple Tech Ltd. + // + { + shared_ptr<employer> er (new employer ("Simple Tech Ltd", "ST")); + + shared_ptr<employee> john (new employee ("John", "Doe", er)); + shared_ptr<employee> jane (new employee ("Jane", "Doe", er)); + + transaction t (db->begin ()); + + db->persist (er); + db->persist (john); + db->persist (jane); + + t.commit (); + } + + // Complex Systems Inc. + // + { + shared_ptr<employer> er (new employer ("Complex Systems Inc", "CS")); + + shared_ptr<employee> john (new employee ("John", "Smith", er)); + shared_ptr<employee> jane (new employee ("Jane", "Smith", er)); + + transaction t (db->begin ()); + + db->persist (er); + db->persist (john); + db->persist (jane); + + t.commit (); + } + + { + session s; + shared_ptr<employer> st, cs; + shared_ptr<employee> ste, cse; + + { + transaction t (db->begin ()); + + st = db->load<employer> ("Simple Tech Ltd"); +#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT + ste = db->load<employee> (st->employees ()[0].object_id ()); +#else + ste = db->load<employee> (st->employees ()[0].object_id<employee> ()); +#endif + + // Test object cache. + // + shared_ptr<employee> e (st->employees ()[0].load ()); + assert (ste->employer () == st); + assert (ste == e); + + t.commit (); + } + + { + transaction t (db->begin ()); + + cs = db->load<employer> ("Complex Systems Inc"); +#ifdef ODB_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGUMENT + cse = db->load<employee> (cs->employees ()[0].object_id ()); +#else + cse = db->load<employee> (cs->employees ()[0].object_id<employee> ()); +#endif + cs->employees ()[0].load (); + + t.commit (); + } + + cs->symbol ("CSI"); + + // Swap employees. + // + ste->employer (cs); + cse->employer (st); + st->employees ()[0] = cse; + cs->employees ()[0] = ste; + + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 3); + t.commit (); + } + + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 0); + t.commit (); + } + + cs->symbol ("COMP"); + st->symbol ("SMPL"); + + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 2); + t.commit (); + } + + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 0); + t.commit (); + } + + // Explicit update. + // + cs->symbol ("CS"); + st->symbol ("ST"); + + { + transaction t (db->begin ()); + db->update (cs); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 1); + t.commit (); + } + + // Rollback after update. + // + cs->symbol ("CSI"); + + try + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 1); + throw failed (); + t.commit (); + } + catch (const failed&) + { + transaction t (db->begin ()); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 1); + t.commit (); + } + } + + // Test session destruction before transaction is commited. + // + { + transaction t (db->begin ()); + { + session s; + shared_ptr<employer> st (db->load<employer> ("Simple Tech Ltd")); + st->symbol ("STL"); + tracer.count = 0; + t.tracer (tracer); + s.flush (*db); + assert (tracer.count == 1); + } + t.commit (); + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/session/custom/session.cxx b/odb-tests/common/session/custom/session.cxx new file mode 100644 index 0000000..1a08c79 --- /dev/null +++ b/odb-tests/common/session/custom/session.cxx @@ -0,0 +1,57 @@ +// file : common/session/custom/session.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +#include <cassert> + +#include "session.hxx" + +session* session::current; + +session:: +session () + : tran_ (0) +{ + assert (current == 0); + current = this; +} + +session:: +~session () +{ + // Unregister from transaction. + // + if (tran_ != 0) + tran_->callback_unregister (this); + + assert (current == this); + current = 0; +} + +void session:: +flush (odb::database& db) +{ + bool flushed (false); + + for (type_map::iterator i (map_.begin ()), e (map_.end ()); i != e; ++i) + { + bool r (i->second->flush (db)); + flushed = flushed || r; + } + + // If we flushed anything, then register the post-commit/rollback callback. + // + if (flushed) + { + tran_ = &odb::transaction::current (); + tran_->callback_register ( + &mark, this, odb::transaction::event_all, 0, &tran_); + } +} + +void session:: +mark (unsigned short event, void* key, unsigned long long) +{ + session& s (*static_cast<session*> (key)); + for (type_map::iterator i (s.map_.begin ()), e (s.map_.end ()); i != e; ++i) + i->second->mark (event); +} diff --git a/odb-tests/common/session/custom/session.hxx b/odb-tests/common/session/custom/session.hxx new file mode 100644 index 0000000..2d2f597 --- /dev/null +++ b/odb-tests/common/session/custom/session.hxx @@ -0,0 +1,191 @@ +// file : common/session/custom/session.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef SESSION_HXX +#define SESSION_HXX + +#include <map> +#include <memory> +#include <typeinfo> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <odb/traits.hxx> // odb::object_traits +#include <odb/details/type-info.hxx> // odb::details::type_info_comparator + +// This custom session implementation assumes we are working with +// one database at a time. +// +class session +{ +public: + session (); + ~session (); + +private: + session (const session&); + session& operator= (const session&); + + // Session for the current thread. This can be implemented in pretty + // much any way that makes sense to the application. It can be a global + // session as we have here. In multi-threaded applications we could use + // TLS instead. + // +public: + static session* current; + + // Change tracking interface. + // +public: + // Call flush() within a transaction to apply the changes to the + // database. + // + void + flush (odb::database&); + +private: + struct object_map_base + { + virtual + ~object_map_base () {} + + // Return true if we flushed anything. + // + virtual bool + flush (odb::database&) = 0; + + virtual void + mark (unsigned short event) = 0; + }; + + enum object_state + { + tracking, // Tracking any modifications by storing the original copy. + changed, // Known to be changed. + flushed // Flushed but not yet committed/rolled back. + }; + + template <typename T> + struct object_data + { + typedef typename odb::object_traits<T>::pointer_type pointer_type; + + explicit + object_data (pointer_type o): obj (o), state (tracking) {} + + pointer_type obj; + pointer_type orig; + object_state state; + }; + + template <typename T> + struct object_map: object_map_base, + std::map<typename odb::object_traits<T>::id_type, + object_data<T> > + { + virtual bool + flush (odb::database&); + + virtual void + mark (unsigned short event); + }; + + // Object cache interface. + // +public: + static bool + _has_cache () {return current != 0;} + + template <typename T> + struct cache_position + { + typedef object_map<T> map; + typedef typename map::iterator iterator; + + cache_position (): map_ (0) {} + cache_position (map& m, const iterator& p): map_ (&m), pos_ (p) {} + + cache_position (const cache_position& p) + : map_ (p.map_) + { + // It might not be ok to use an uninitialized iterator. + // + if (p.map_ != 0) + pos_ = p.pos_; + } + + cache_position& + operator= (const cache_position& p) + { + // It might not be ok to use an uninitialized iterator on the rhs. + // + if (p.map_ != 0) + pos_ = p.pos_; + map_ = p.map_; + return *this; + } + + map* map_; + iterator pos_; + }; + + // Cache management. + // + template <typename T> + static cache_position<T> + _cache_insert (odb::database&, + const typename odb::object_traits<T>::id_type&, + const typename odb::object_traits<T>::pointer_type&); + + template <typename T> + static typename odb::object_traits<T>::pointer_type + _cache_find (odb::database&, const typename odb::object_traits<T>::id_type&); + + template <typename T> + static void + _cache_erase (const cache_position<T>& p) + { + if (p.map_ != 0) + p.map_->erase (p.pos_); + } + + // Notifications. + // + template <typename T> + static void + _cache_persist (const cache_position<T>& p) + { + _cache_load (p); + } + + template <typename T> + static void + _cache_load (const cache_position<T>&); + + template <typename T> + static void + _cache_update (odb::database&, const T&); + + template <typename T> + static void + _cache_erase (odb::database&, + const typename odb::object_traits<T>::id_type&); + +private: + // Post-commit/rollback callback. + // + static void + mark (unsigned short event, void* key, unsigned long long); + +private: + typedef std::map<const std::type_info*, + std::shared_ptr<object_map_base>, + odb::details::type_info_comparator> type_map; + type_map map_; + odb::transaction* tran_; +}; + +#include "session.txx" + +#endif // SESSION_HXX diff --git a/odb-tests/common/session/custom/session.txx b/odb-tests/common/session/custom/session.txx new file mode 100644 index 0000000..65ab933 --- /dev/null +++ b/odb-tests/common/session/custom/session.txx @@ -0,0 +1,159 @@ +// file : common/session/custom/session.txx +// license : GNU GPL v2; see accompanying LICENSE file + +#include <cassert> + +template <typename T> +typename session::cache_position<T> session:: +_cache_insert (odb::database&, + const typename odb::object_traits<T>::id_type& id, + const typename odb::object_traits<T>::pointer_type& obj) +{ + if (current == 0) + return cache_position<T> (); // No session, return empty position. + + std::shared_ptr<object_map_base>& pm (current->map_[&typeid (T)]); + + if (!pm) + pm.reset (new object_map<T>); + + object_map<T>& m (static_cast<object_map<T>&> (*pm)); + + typename object_map<T>::value_type vt (id, object_data<T> (obj)); + std::pair<typename object_map<T>::iterator, bool> r (m.insert (vt)); + + // We shall never try to re-insert the same object into the cache. + // + assert (r.second); + + return cache_position<T> (m, r.first); +} + +template <typename T> +typename odb::object_traits<T>::pointer_type session:: +_cache_find (odb::database&, const typename odb::object_traits<T>::id_type& id) +{ + typedef typename odb::object_traits<T>::pointer_type pointer_type; + + if (current == 0) + return pointer_type (); // No session, return NULL pointer. + + type_map::const_iterator ti (current->map_.find (&typeid (T))); + + if (ti == current->map_.end ()) + return pointer_type (); + + const object_map<T>& m (static_cast<const object_map<T>&> (*ti->second)); + typename object_map<T>::const_iterator oi (m.find (id)); + + if (oi == m.end ()) + return pointer_type (); + + return oi->second.obj; +} + +template <typename T> +void session:: +_cache_load (const cache_position<T>& p) +{ + typedef typename odb::object_traits<T>::pointer_type pointer_type; + + if (p.map_ == 0) + return; // Empty position. + + // Make a copy for change tracking. If our object model had a + // polymorphic hierarchy, then we would have had to use a + // virtual function-based mechanism (e.g., clone()) instead of + // the copy constructor since for a polymorphic hierarchy all + // the derived objects are stored as pointers to the root object. + // + p.pos_->second.orig = pointer_type (new T (*p.pos_->second.obj)); +} + +template <typename T> +void session:: +_cache_update (odb::database&, const T& obj) +{ + typedef odb::object_traits<T> object_traits; + typedef typename object_traits::pointer_type pointer_type; + + if (current == 0) + return; // No session. + + // User explicitly updated the object by calling database::update(). + // Change the state to flushed and reset the original copy (we are + // still tracking changes after the update). + // + type_map::iterator ti (current->map_.find (&typeid (T))); + + if (ti == current->map_.end ()) + return; // This object is not in the session. + + object_map<T>& m (static_cast<object_map<T>&> (*ti->second)); + typename object_map<T>::iterator oi (m.find (object_traits::id (obj))); + + if (oi == m.end ()) + return; // This object is not in the session. + + object_data<T>& d (oi->second); + d.orig = pointer_type (new T (*d.obj)); + d.state = flushed; +} + +template <typename T> +void session:: +_cache_erase (odb::database&, + const typename odb::object_traits<T>::id_type& id) +{ + if (current == 0) + return; // No session. + + type_map::iterator ti (current->map_.find (&typeid (T))); + + if (ti == current->map_.end ()) + return; + + object_map<T>& m (static_cast<object_map<T>&> (*ti->second)); + typename object_map<T>::iterator oi (m.find (id)); + + if (oi == m.end ()) + return; + + m.erase (oi); + + if (m.empty ()) + current->map_.erase (ti); +} + +template <typename T> +bool session::object_map<T>:: +flush (odb::database& db) +{ + bool r (false); + for (typename object_map<T>::iterator i (this->begin ()), e (this->end ()); + i != e; ++i) + { + object_data<T>& d (i->second); + + if (d.state == changed || d.obj->changed (*d.orig)) + db.update (d.obj); // State changed by the update() notification. + + r = r || d.state == flushed; + } + + return r; +} + +template <typename T> +void session::object_map<T>:: +mark (unsigned short event) +{ + for (typename object_map<T>::iterator i (this->begin ()), e (this->end ()); + i != e; ++i) + { + object_data<T>& d (i->second); + + if (d.state == flushed) + d.state = event == odb::transaction::event_commit ? tracking : changed; + } +} diff --git a/odb-tests/common/session/custom/test.hxx b/odb-tests/common/session/custom/test.hxx new file mode 100644 index 0000000..3f2703f --- /dev/null +++ b/odb-tests/common/session/custom/test.hxx @@ -0,0 +1,118 @@ +// file : common/session/custom/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <memory> +#include <vector> + +#include <odb/core.hxx> +#include <odb/lazy-ptr.hxx> + +class employee; + +#pragma db object pointer(std::shared_ptr) session +class employer +{ +public: + employer (const std::string& name, const std::string& symbol) + : name_ (name), symbol_ (symbol) {} + + const std::string& + name () const {return name_;} + + const std::string& + symbol () const {return symbol_;} + + void + symbol (const std::string& symbol) {symbol_ = symbol;} + + // Employees of this employer. + // + typedef std::vector<odb::lazy_weak_ptr<employee>> employees_type; + + const employees_type& + employees () const {return employees_;} + + employees_type& + employees () {return employees_;} + + // Change tracking. + // +public: + bool + changed (const employer& orig) const + { + // Note that we don't need to track object ids, inverse pointers, nor + // readonly/const data members. + // + return symbol_ != orig.symbol_; + } + +private: + friend class odb::access; + employer () {} + + #pragma db id + std::string name_; + + std::string symbol_; + + #pragma db value_not_null inverse(employer_) + employees_type employees_; +}; + +#pragma db object pointer(std::shared_ptr) session +class employee +{ +public: + typedef ::employer employer_type; + + employee (const std::string& first, + const std::string& last, + std::shared_ptr<employer_type> employer) + : first_ (first), last_ (last), employer_ (employer) {} + + // Name. + // + const std::string& + first () const {return first_;} + + const std::string& + last () const {return last_;} + + // Employer. + // + std::shared_ptr<employer_type> + employer () const {return employer_;} + + void + employer (std::shared_ptr<employer_type> e) {employer_ = e;} + + // Change tracking. + // +public: + bool + changed (const employee& orig) const + { + return first_ != orig.first_ || last_ != orig.last_ || + employer_ != orig.employer_; + } + +private: + friend class odb::access; + employee () {} + + #pragma db id auto + unsigned long id_; + + std::string first_; + std::string last_; + + #pragma db not_null + std::shared_ptr<employer_type> employer_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/session/custom/testscript b/odb-tests/common/session/custom/testscript new file mode 100644 index 0000000..c3f3e06 --- /dev/null +++ b/odb-tests/common/session/custom/testscript @@ -0,0 +1,53 @@ +# file : common/session/custom/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/statement/processing/buildfile b/odb-tests/common/statement/processing/buildfile new file mode 100644 index 0000000..97124e8 --- /dev/null +++ b/odb-tests/common/statement/processing/buildfile @@ -0,0 +1,8 @@ +# file : common/statement/processing/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libs = libodb%lib{odb} + +exe{driver}: {hxx cxx}{*} $libs testscript + +cxx.poptions =+ "-I$out_base" "-I$src_base" diff --git a/odb-tests/common/statement/processing/driver.cxx b/odb-tests/common/statement/processing/driver.cxx new file mode 100644 index 0000000..2d00107 --- /dev/null +++ b/odb-tests/common/statement/processing/driver.cxx @@ -0,0 +1,619 @@ +// file : common/statement/processing/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test internal statement processing machinery. +// + +#include <string> +#include <iostream> + +#include <odb/statement.hxx> + +#undef NDEBUG +#include <cassert> + +using namespace std; + +static bool +insert (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_insert ( + r, stmt, bind, bind_size, sizeof (void*), '$'); + return r == expected; +} + +static bool +update (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_update ( + r, stmt, bind, bind_size, sizeof (void*), '$'); + return r == expected; +} + +static bool +select (const char* stmt, + const char* expected, + const void* const* bind, + size_t bind_size) +{ + string r; + odb::statement::process_select ( + r, stmt, bind, bind_size, sizeof (void*), '[', ']', true); + return r == expected; +} + +int +main (int, char* argv[]) +{ + // + // INSERT + // + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "VALUES\n" + "(DEFAULT,\n$1)", + "INSERT INTO [foo] ([a], [b]) VALUES (DEFAULT, $1)", + b, 2)); + } + + // Empty via statement. + // + /* LIBODB_DEBUG_STATEMENT_PROCESSING + { + assert (insert ("INSERT INTO [foo]\n" + "DEFAULT VALUES", + "INSERT INTO [foo] DEFAULT VALUES", + 0, 0)); + } + */ + + // Empty via bind. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a])\n" + "VALUES\n" + "($1)", + "INSERT INTO [foo] DEFAULT VALUES", + b, 1)); + } + + // Empty with OUTPUT. + // + { + void* b[] = {0, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "OUTPUT INSERTED.[id]\n" + "VALUES\n" + "($1,\n$2)", + "INSERT INTO [foo] OUTPUT INSERTED.[id] DEFAULT VALUES", + b, 2)); + } + + // Empty with RETURNING. + // + { + void* b[] = {0, 0, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$1,\n$2)\n" + "RETURNING [id]", + "INSERT INTO [foo] DEFAULT VALUES RETURNING [id]", + b, 3)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b])\n" + "VALUES\n" + "(1,\n$1)", + "INSERT INTO [foo] ([a]) VALUES (1)", + b, 1)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "(1,\n$1,\nDEFAULT)", + "INSERT INTO [foo] ([a], [c]) VALUES (1, DEFAULT)", + b, 1)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([b], [c]) VALUES ($2, $3)", + b, 3)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [b]) VALUES ($1, $2)", + b, 3)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3)", + b, 3)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv, 0}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c],\n[d],\n[e])\n" + "VALUES\n" + "($1,\n$2,\n$3,\n$4,\n$5)", + "INSERT INTO [foo] ([b], [d]) VALUES ($2, $4)", + b, 5)); + } + + // Not present and OUTPUT. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "OUTPUT INSERTED.[id]\n" + "VALUES\n" + "($1,\n$2,\n$3)", + "INSERT INTO [foo] ([a], [c]) OUTPUT INSERTED.[id] " + "VALUES ($1, $3)", + b, 3)); + } + + // Not present and RETURNING. + // + { + void* b[] = {argv, 0, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\n$2,\n$3)\n" + "RETURNING [id]", + "INSERT INTO [foo] ([a], [c]) VALUES ($1, $3) " + "RETURNING [id]", + b, 3)); + } + + // Value expressions. + // + { + void* b[] = {argv, argv, argv}; + assert (insert ("INSERT INTO [foo]\n" + "([a],\n[b],\n[c])\n" + "VALUES\n" + "($1,\nCAST($2, TEXT),\n$3)", + "INSERT INTO [foo] ([a], [b], [c]) " + "VALUES ($1, CAST($2, TEXT), $3)", + b, 3)); + } + + // + // UPDATE + // + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "ver=ver+1,\n[a]=$1\n" + "WHERE [id]=$2", + "UPDATE [foo] SET ver=ver+1, [a]=$1 WHERE [id]=$2", + b, 2)); + } + + // Empty via bind. + // + { + void* b[] = {0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1\n" + "WHERE [id]=$2", + "", + b, 2)); + } + + // Empty via bind, but not values. + // + { + void* b[] = {0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "ver=ver+1,\n[a]=$1\n" + "WHERE [id]=$2", + "UPDATE [foo] SET ver=ver+1 WHERE [id]=$2", + b, 2)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [b]=$2, [c]=$3 WHERE [id]=$4", + b, 4)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [b]=$2 WHERE [id]=$4", + b, 4)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [c]=$3 WHERE [id]=$4", + b, 4)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3,\n" + "[d]=$4\n" + "WHERE [id]=$5", + "UPDATE [foo] SET [b]=$2, [d]=$4 WHERE [id]=$5", + b, 5)); + } + + // Not present and OUTPUT. + // + { + void* b[] = {argv, 0, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3\n" + "OUTPUT INSERTED.[ver] " + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [c]=$3 OUTPUT INSERTED.[ver] " + "WHERE [id]=$4", + b, 4)); + } + + // Value expressions. + // + { + void* b[] = {argv, argv, argv, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=CAST($2, TEXT),\n" + "[c]=$3\n" + "WHERE [id]=$4", + "UPDATE [foo] SET [a]=$1, [b]=CAST($2, TEXT), [c]=$3 " + "WHERE [id]=$4", + b, 4)); + } + + // No OUTPUT/WHERE clause. + // + { + void* b[] = {argv, 0, argv}; + assert (update ("UPDATE [foo]\n" + "SET\n" + "[a]=$1,\n" + "[b]=$2,\n" + "[c]=$3", + "UPDATE [foo] SET [a]=$1, [c]=$3", + b, 4)); + } + + // + // SELECT + // + + // Empty. + // + { + void* b[] = {0, 0, 0}; + assert (select ("SELECT\n" + "[a].[x],\n" + "[t].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "", + b, 3)); + } + + // Fast path. + // + { + void* b[] = {argv, argv}; + assert (select ("SELECT\n" + "[s].[t].[x],\n" + "[a].[y]\n" + "FROM [s].[t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id]\n" + "WHERE [s].[t].[id]=$1", + "SELECT [s].[t].[x], [a].[y] FROM [s].[t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[t].[id] " + "WHERE [s].[t].[id]=$1", + b, 2)); + } + + // First not present. + // + { + void* b[] = {0, argv, argv}; + assert (select ("SELECT\n" + "[a].[x],\n" + "[t].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[y], [t].[z] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Last not present. + // + { + void* b[] = {argv, argv, 0}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[t].[y],\n" + "[a].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [t].[y] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Middle not present. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [t].[z] FROM [t] WHERE [t].[id]=$1", + b, 3)); + } + + // Multiple not present. + // + { + void* b[] = {0, argv, 0, argv}; + assert (select ("SELECT\n" + "[a1].[w],\n" + "[t].[x],\n" + "[a2].[y],\n" + "[a3].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a1] ON [a1].[id]=[t].[id]\n" + "LEFT JOIN [t2] AS [a2] ON [a2].[id]=[t].[id]\n" + "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [a3].[z] FROM [t] " + "LEFT JOIN [t3] AS [a3] ON [a3].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 4)); + } + + // Column expression. + // + { + void* b[] = {argv, argv, 0}; + assert (select ("SELECT\n" + "[t].[x],\n" + "CAST([a].[y], TEXT),\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], CAST([a].[y], TEXT) FROM [t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // No WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[t].[y],\n" + "[t].[z]\n" + "FROM [t]", + "SELECT [t].[x], [t].[z] FROM [t]", + b, 3)); + } + + // JOIN without WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]", + "SELECT [t].[x], [t].[z] FROM [t]", + b, 3)); + } + + // JOIN presence because of WHERE. + // + { + void* b[] = {argv, 0, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "WHERE [t].[id]=$1 AND [a].[id]=$2", + "SELECT [t].[x], [t].[z] FROM [t] " + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id] " + "WHERE [t].[id]=$1 AND [a].[id]=$2", + b, 3)); + } + + + // JOIN presence because of dependent JOIN. + // + { + void* b[] = {argv, argv, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[a_b].[y],\n" + "[t].[z]\n" + "FROM [t]\n" + "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id]\n" + "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [a_b].[y], [t].[z] FROM [t] " + "LEFT JOIN [d] AS [a_d] ON [a_d].[id]=[t].[id] " + "LEFT JOIN [b] AS [a_b] ON [a_b].[id]=[a_d].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // JOIN without alias and with schema. + // + { + void* b[] = {argv, argv, argv}; + assert (select ("SELECT\n" + "[t].[x],\n" + "[s].[t1].[y],\n" + "[t2].[z]\n" + "FROM [t]\n" + "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id]\n" + "LEFT JOIN [t2] ON [t2].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [t].[x], [s].[t1].[y], [t2].[z] FROM [t] " + "LEFT JOIN [s].[t1] ON [s].[t1].[id]=[t].[id] " + "LEFT JOIN [t2] ON [t2].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 3)); + } + + // JOIN alias top-level qualifer test. + // + { + void* b[] = {argv, 0}; + assert (select ("SELECT\n" + "[s].[a].[x],\n" + "[a].[y]\n" + "FROM [s].[a]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[s].[a].[id]\n" + "WHERE [s].[a].[id]=$1", + "SELECT [s].[a].[x] FROM [s].[a] WHERE [s].[a].[id]=$1", + b, 2)); + } + + // JOIN alias bottom-level qualifer test (FROM case). + // + { + void* b[] = {argv, 0}; + assert (select ("SELECT\n" + "[a].[t].[x],\n" + "[a].[y]\n" + "FROM [a].[t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[a].[t].[id]\n" + "WHERE [a].[t].[id]=$1", + "SELECT [a].[t].[x] FROM [a].[t] WHERE [a].[t].[id]=$1", + b, 2)); + } + + // JOIN alias bottom-level qualifer test (LEFT JOIN case). + // + { + void* b[] = {0, argv}; + assert (select ("SELECT\n" + "[a].[y],\n" + "[a].[t2].[x]\n" + "FROM [t]\n" + "LEFT JOIN [t1] AS [a] ON [a].[id]=[t].[id]\n" + "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id]\n" + "WHERE [t].[id]=$1", + "SELECT [a].[t2].[x] FROM [t] " + "LEFT JOIN [a].[t2] ON [a].[t2].[id]=[t].[id] " + "WHERE [t].[id]=$1", + b, 2)); + } +} diff --git a/odb-tests/common/statement/processing/testscript b/odb-tests/common/statement/processing/testscript new file mode 100644 index 0000000..2460dc6 --- /dev/null +++ b/odb-tests/common/statement/processing/testscript @@ -0,0 +1,6 @@ +# file : common/statement/processing/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +: basics +: +$* diff --git a/odb-tests/common/threads/buildfile b/odb-tests/common/threads/buildfile new file mode 100644 index 0000000..53b98ec --- /dev/null +++ b/odb-tests/common/threads/buildfile @@ -0,0 +1,49 @@ +# file : common/threads/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 threads_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# While we don't call any pthread_*() functions, this appears to be needed for +# some std::thread implementations (like libstdc++). Note that +# odb::details::thread inlines some std::thread API calls. +# +if ($cxx.target.class != 'windows') + cxx.libs += -pthread + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc diff --git a/odb-tests/common/threads/driver.cxx b/odb-tests/common/threads/driver.cxx new file mode 100644 index 0000000..1add011 --- /dev/null +++ b/odb-tests/common/threads/driver.cxx @@ -0,0 +1,236 @@ +// file : common/threads/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test operations in a multi-threaded environment. +// + +#include <vector> +#include <memory> // std::unique_ptr +#include <cstddef> // std::size_t +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <odb/details/shared-ptr.hxx> +#include <odb/details/thread.hxx> + +#include <libcommon/config.hxx> // DATABASE_* +#include <libcommon/common.hxx> + +#if defined(DATABASE_SQLITE) +# include <odb/sqlite/database.hxx> +#endif + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; +namespace details = odb::details; + +const unsigned long thread_count = 24; +const unsigned long iteration_count = 30; +const unsigned long sub_iteration_count = 40; + +struct task +{ + task (database& db, unsigned long n) + : db_ (db), n_ (n) + { + } + + void* + execute () + { + try + { + for (unsigned long i (0); i < iteration_count; ++i) + { + unsigned long id ((n_ * iteration_count + i) * 3); + + object o1 (id, "first object"); + object o2 (id + 1, "second object"); + object o3 (id + 2, "third object"); + + // The following transactions may lead to deadlocks. + // + while (true) + { + try + { + transaction t (db_.begin ()); + + db_.persist (o1); + db_.persist (o2); + db_.persist (o3); + t.commit (); + break; + } + catch (const deadlock&) {} + } + + while (true) + { + try + { +#if !defined(DATABASE_SQLITE) + transaction t (db_.begin ()); +#else + // SQLite has a peculiar table locking mode (shared cache) + // which can lead to any of the transactions in this test + // deadlocking even though they shouldn't from the user's + // perspective. One way to work around this problem is to + // start a "write" transaction as such right away. + // + transaction t; + + if (db_.id () != odb::id_sqlite) + t.reset (db_.begin ()); + else + { + t.reset ( + static_cast<odb::sqlite::database&> (db_).begin_immediate ()); + } +#endif + unique_ptr<object> o (db_.load<object> (id)); + assert (o->str_ == "first object"); + o->str_ = "another value"; + db_.update (*o); + t.commit (); + break; + } + catch (const deadlock&) {} + } + + for (unsigned long j (0); j < sub_iteration_count; ++j) + { + typedef odb::query<object> query; + typedef odb::prepared_query<object> prep_query; + typedef odb::result<object> result; + + while (true) + { + try + { + transaction t (db_.begin ()); + + prep_query pq (db_.lookup_query<object> ("object-query")); + + if (!pq) + { + pq = db_.prepare_query<object> ( + "object-query", query::str == "another value"); + db_.cache_query (pq); + } + + result r (pq.execute (false)); + + bool found (false); + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + if (i->id_ == id) + { + found = true; + break; + } + } + assert (found); + t.commit (); + break; + } + catch (const deadlock&) {} + } + } + + while (true) + { + try + { + transaction t (db_.begin ()); + db_.erase<object> (id); + t.commit (); + break; + } + catch (const deadlock&) {} + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return reinterpret_cast<void*> (1); + } + + return 0; + } + + static void* + execute (void* arg) + { + return static_cast<task*> (arg)->execute (); + } + + database& db_; + unsigned long n_; +}; + +bool +test (int argc, char* argv[], size_t max_connections) +{ + unique_ptr<database> db (create_database (argc, argv, true, max_connections)); + + vector<details::shared_ptr<details::thread> > threads; + vector<details::shared_ptr<task> > tasks; + + for (unsigned long i (0); i < thread_count; ++i) + { + details::shared_ptr<task> t (new (details::shared) task (*db, i)); + tasks.push_back (t); + + threads.push_back ( + details::shared_ptr<details::thread> ( + new (details::shared) details::thread (&task::execute, t.get ()))); + } + + bool r (true); + + for (unsigned long i (0); i < thread_count; ++i) + if (threads[i]->join () != 0) + r = false; + + { + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> ()); + + for (result::iterator i (r.begin ()); i != r.end (); ++i) + db->erase<object> (i->id_); + + t.commit (); + } + + return r; +} + +int +main (int argc, char* argv[]) +{ + try + { + if (!(test (argc, argv, 0) && + test (argc, argv, thread_count - 1) && + test (argc, argv, thread_count / 2) && + test (argc, argv, thread_count / 4))) + return 1; + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/threads/test.hxx b/odb-tests/common/threads/test.hxx new file mode 100644 index 0000000..2ed6e67 --- /dev/null +++ b/odb-tests/common/threads/test.hxx @@ -0,0 +1,29 @@ +// file : common/template/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> + +#include <odb/core.hxx> + +#pragma db object +struct object +{ + object (unsigned long id, const std::string& str) + : id_ (id), str_ (str) + { + } + + object () + { + } + + #pragma db id + unsigned long id_; + + std::string str_; +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/threads/testscript b/odb-tests/common/threads/testscript new file mode 100644 index 0000000..0d619ab --- /dev/null +++ b/odb-tests/common/threads/testscript @@ -0,0 +1,70 @@ +# file : common/threads/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + # Note: this is quite slow: + # + # $ time ./driver --database ~/odb-test.db + # real 3m5.593s + # user 1m1.244s + # sys 0m26.793s + # + # $ time ./driver --database /tmp/odb-test.db + # real 0m13.909s + # user 0m16.724s + # sys 0m4.874s + # + # $ time ./driver --database "file::memory:" + # real 0m12.406s + # user 0m15.694s + # sys 0m4.207s + # + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/transaction/basics/buildfile b/odb-tests/common/transaction/basics/buildfile new file mode 100644 index 0000000..f412235 --- /dev/null +++ b/odb-tests/common/transaction/basics/buildfile @@ -0,0 +1,13 @@ +# file : common/transaction/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libs = libodb%lib{odb} +import libs += lib{common} + +exe{driver}: {hxx cxx}{*} $libs testscript + +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/transaction/basics/driver.cxx b/odb-tests/common/transaction/basics/driver.cxx new file mode 100644 index 0000000..1833555 --- /dev/null +++ b/odb-tests/common/transaction/basics/driver.cxx @@ -0,0 +1,151 @@ +// file : common/transaction/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test basic transaction operations. +// + +#include <string> +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/tracer.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/statement.hxx> +#include <odb/exceptions.hxx> + +#include <libcommon/common.hxx> +#include <libcommon/concrete.hxx> + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct transaction_tracer: odb::tracer +{ + virtual void + execute (connection&, const char* s) + { + string str (s); + + if (str == "BEGIN") + cout << "begin transaction" << endl; + else if (str == "COMMIT") + cout << "commit transaction" << endl; + else if (str == "ROLLBACK") + cout << "rollback transaction" << endl; + } + + // Override the other version to get rid of a Sun CC warning. + // + virtual void + execute (connection& c, const statement& s) + { + execute (c, s.text ()); + } +}; + +int +main (int argc, char* argv[]) +{ + { + transaction_tracer tracer; + unique_ptr<database> db (create_database (argc, argv, false)); + db->tracer (tracer); + + assert (!transaction::has_current ()); + + // Current and db accessors. + // + cout << "test 001" << endl; + { + transaction t (db->begin ()); + assert (&t.database () == db.get ()); + assert (transaction::has_current ()); + assert (&transaction::current () == &t); + + transaction::reset_current (); + assert (!transaction::has_current ()); + + transaction t2 (db->begin (), false); + assert (!transaction::has_current ()); + + transaction::current (t2); + assert (&transaction::current () == &t2); + } + + // Commit. + // + cout << "test 002" << endl; + { + transaction t (db->begin ()); + t.commit (); + } + + // Rollback. + // + cout << "test 003" << endl; + { + transaction t (db->begin ()); + t.rollback (); + } + + // Auto rollback. + // + cout << "test 004" << endl; + { + transaction t (db->begin ()); + } + + // Nested transaction. + // + cout << "test 005" << endl; + { + transaction t (db->begin ()); + + try + { + transaction n (db->begin ()); + } + catch (const already_in_transaction&) + { + cout << "already_in_transaction" << endl; + } + } + + // Concrete transaction type. + // + cout << "test 006" << endl; + { + assert (sizeof (odb_db::transaction) == sizeof (transaction)); + + odb_db::transaction t (static_cast<odb_db::database&> (*db).begin ()); + odb_db::transaction& r (odb_db::transaction::current ()); + assert (&t == &r); + } + + // Transaction restart. + // + cout << "test 007" << endl; + { + transaction t (db->begin ()); + t.commit (); + t.reset (db->begin ()); + t.commit (); + } + } + + // Test early connection release. + // + { + unique_ptr<database> db (create_database (argc, argv, false, 1)); + transaction t1 (db->begin ()); + t1.commit (); + transaction t2 (db->begin ()); + t2.rollback (); + transaction t3 (db->begin ()); + t3.commit (); + } +} diff --git a/odb-tests/common/transaction/basics/testscript b/odb-tests/common/transaction/basics/testscript new file mode 100644 index 0000000..873f471 --- /dev/null +++ b/odb-tests/common/transaction/basics/testscript @@ -0,0 +1,80 @@ +# file : common/transaction/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + ++cat <<EOI >=output + test 001 + begin transaction + begin transaction + rollback transaction + rollback transaction + test 002 + begin transaction + commit transaction + test 003 + begin transaction + rollback transaction + test 004 + begin transaction + rollback transaction + test 005 + begin transaction + already_in_transaction + rollback transaction + test 006 + begin transaction + rollback transaction + test 007 + begin transaction + commit transaction + begin transaction + commit transaction + EOI + +test.redirects += >>>../output + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $* +} diff --git a/odb-tests/common/transaction/callback/buildfile b/odb-tests/common/transaction/callback/buildfile new file mode 100644 index 0000000..78b1b03 --- /dev/null +++ b/odb-tests/common/transaction/callback/buildfile @@ -0,0 +1,13 @@ +# file : common/transaction/callback/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libs = libodb%lib{odb} +import libs += lib{common} + +exe{driver}: {hxx cxx}{*} $libs testscript + +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/transaction/callback/driver.cxx b/odb-tests/common/transaction/callback/driver.cxx new file mode 100644 index 0000000..d0af993 --- /dev/null +++ b/odb-tests/common/transaction/callback/driver.cxx @@ -0,0 +1,231 @@ +// file : common/transaction/callback/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test transaction callbacks. +// + +#include <memory> // std::unique_ptr +#include <cstddef> // std::size_t +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +struct callback +{ + callback (unsigned short v): v_ (v), t_ (0) {} + callback (unsigned short v, transaction& t): v_ (v), t_ (0) {register_ (t);} + ~callback () {if (t_ != 0) unregister ();} + + void + register_ (transaction& t) + { + t_ = &t; + t.callback_register (&func, this, transaction::event_all, v_, &t_); + } + + void + unregister () + { + cout << " unregister callback " << v_ << endl; + t_->callback_unregister (this); + t_ = 0; + } + + void + update (unsigned short v) + { + v_ = v; + t_->callback_update (this, transaction::event_all, v_, &t_); + } + +private: + static void + func (unsigned short event, void* key, unsigned long long data) + { + callback& c (*static_cast<callback*> (key)); + + const char* en; + switch (event) + { + case transaction::event_commit: + en = "commit"; + break; + case transaction::event_rollback: + en = "rollback"; + break; + default: + en = "unknown"; + } + + cout << " callback " << c.v_ << " " << en << endl; + + assert (data == c.v_); + assert (c.t_ == 0); + } + + unsigned short v_; + transaction* t_; +}; + +struct failed {}; + +static void +throw_func (unsigned short, void*, unsigned long long) +{ + throw failed (); +} + +static void +dummy_func (unsigned short, void* key, unsigned long long data) +{ + assert (reinterpret_cast<unsigned long long> (key) == data); +} + +static void +fill (transaction& t) +{ + // 20 is from odb/transaction.hxx. + // + for (size_t i (0); i < 20; ++i) + t.callback_register (&dummy_func, + reinterpret_cast<void*> (i), + transaction::event_all, + i); +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv, false)); + + // We want to test both stack and dynamic slots. + // + for (unsigned short i (1); i < 3; ++i) + { + // Test basic logic. + // + cout << "test " << i << "/001" << endl; + + // Commit callback. + // + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + t.commit (); + } + + // Rollback callback. + // + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + t.rollback (); + } + + // Rollback via exception callback. + // + { + callback c1 (1); + + try + { + transaction t (db->begin ()); + if (i == 2) fill (t); + c1.register_ (t); + throw failed (); + } + catch (const failed&) + { + } + } + + // Unregister callback at the end. + // + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + c1.unregister (); + t.callback_unregister (&c1); // Test unregistering non-registered key. + t.commit (); + } + + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + c1.unregister (); + callback c2 (2, t); + t.commit (); + } + + // Unregister callback in the middle. + // + cout << "test " << i << "/002" << endl; + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + callback c2 (2, t); + callback c3 (3, t); + c2.unregister (); + t.commit (); + } + + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + callback c2 (2, t); + callback c3 (3, t); + c2.unregister (); + callback c4 (4, t); // Using the free slot. + t.commit (); + } + + // Test a callback in the middle that throws. + // + cout << "test " << i << "/003" << endl; + try + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c1 (1, t); + t.callback_register (&throw_func, 0); + callback c2 (2, t); + t.commit (); + } + catch (const failed&) + { + } + + // Test callback_update(). + // + { + transaction t (db->begin ()); + if (i == 2) fill (t); + callback c (1, t); + c.update (2); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/transaction/callback/testscript b/odb-tests/common/transaction/callback/testscript new file mode 100644 index 0000000..715d49a --- /dev/null +++ b/odb-tests/common/transaction/callback/testscript @@ -0,0 +1,90 @@ +# file : common/transaction/callback/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + ++cat <<EOI >=output + test 1/001 + callback 1 commit + callback 1 rollback + callback 1 rollback + unregister callback 1 + unregister callback 1 + callback 2 commit + test 1/002 + unregister callback 2 + callback 1 commit + callback 3 commit + unregister callback 2 + callback 1 commit + callback 4 commit + callback 3 commit + test 1/003 + callback 1 commit + callback 2 commit + test 2/001 + callback 1 commit + callback 1 rollback + callback 1 rollback + unregister callback 1 + unregister callback 1 + callback 2 commit + test 2/002 + unregister callback 2 + callback 1 commit + callback 3 commit + unregister callback 2 + callback 1 commit + callback 4 commit + callback 3 commit + test 2/003 + callback 1 commit + callback 2 commit + EOI + +test.redirects += >>>../output + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $* +} diff --git a/odb-tests/common/types/buildfile b/odb-tests/common/types/buildfile new file mode 100644 index 0000000..95fe5b6 --- /dev/null +++ b/odb-tests/common/types/buildfile @@ -0,0 +1,39 @@ +# file : common/types/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 types_ + +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/types/driver.cxx b/odb-tests/common/types/driver.cxx new file mode 100644 index 0000000..bdc66b8 --- /dev/null +++ b/odb-tests/common/types/driver.cxx @@ -0,0 +1,37 @@ +// file : common/types/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test C++ type handling (anonymous types, aliasing). +// + +#include <memory> +#include <iostream> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +template <typename T1, typename T2> +struct same_p +{ + static const bool result = false; +}; + +template <typename T> +struct same_p<T, T> +{ + static const bool result = true; +}; + +int +main () +{ + assert ((same_p<odb::object_traits<object2>::id_type, int>::result)); +} diff --git a/odb-tests/common/types/test.hxx b/odb-tests/common/types/test.hxx new file mode 100644 index 0000000..a99b499 --- /dev/null +++ b/odb-tests/common/types/test.hxx @@ -0,0 +1,55 @@ +// file : common/types/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#ifdef ODB_COMPILER +typedef int int_t; +typedef short num_t; +#else +typedef int num_t; +#endif + +typedef num_t num_type; + +#pragma db object +struct object1 +{ + typedef int int_type; + + #pragma db id + int_type id_; +}; + +#pragma db object +struct object2 +{ + #pragma db id + num_type num_; +}; + +// Template-id with "inner" name (compilation test). +// +template <typename X> +struct num_wrap +{ +#ifdef ODB_COMPILER + typedef num_wrap this_type; // Name that we should not use. +#endif + + num_wrap () {} + num_wrap (X v): v_ (v) {} + operator X () const {return v_;} + + X v_; +}; + +#pragma db object +struct object3 +{ + #pragma db id type("INTEGER") + num_wrap<long long> num_; // Use long long to avoid warnings. +}; + +#endif // TEST_HXX diff --git a/odb-tests/common/types/testscript b/odb-tests/common/types/testscript new file mode 100644 index 0000000..159972b --- /dev/null +++ b/odb-tests/common/types/testscript @@ -0,0 +1,6 @@ +# file : common/types/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +: basic +: +$* diff --git a/odb-tests/common/view/basics/buildfile b/odb-tests/common/view/basics/buildfile new file mode 100644 index 0000000..2cccab2 --- /dev/null +++ b/odb-tests/common/view/basics/buildfile @@ -0,0 +1,45 @@ +# file : common/view/basics/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 t_view_b_ \ + --generate-schema \ + --generate-query \ + --generate-prepared + +if $multi + odb_options += -DMULTI_DATABASE + +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/view/basics/driver.cxx b/odb-tests/common/view/basics/driver.cxx new file mode 100644 index 0000000..419b29d --- /dev/null +++ b/odb-tests/common/view/basics/driver.cxx @@ -0,0 +1,838 @@ +// file : common/view/basics/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test view basics. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> +#include <libcommon/config.hxx> // DATABASE_XXX + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +template <typename V> +void +view1_check (odb::result<V>& r) +{ + typedef odb::result<V> result; + + typename result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first == "Jane" && i->last == "Doe" && i->age == 29); + + assert (++i != r.end ()); + V v; + i.load (v); + assert (v.first == "John" && v.last == "Doe" && v.age == 30); + + assert (++i == r.end ()); +} + +template <typename V> +void +view2_test (const unique_ptr<database>& db) +{ + typedef odb::query<V> query; + typedef odb::result<V> result; + typedef typename result::iterator iterator; + + transaction t (db->begin ()); + + { + result r (db->query<V> ()); + iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->count == 4); + } + + { + result r; + if (db->id () != odb::id_oracle) + r = db->query<V> ("age < 31"); + else + r = db->query<V> ("\"age\" < 31"); + + iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->count == 2); + } + + { + result r (db->query<V> (query::age < 31)); + iterator i (r.begin ()); + assert (i != r.end ()); + assert (i->count == 2); + } + + { + unique_ptr<V> v (db->query_one<V> ()); + assert (v->count == 4); + } + + { + unique_ptr<V> v; + if (db->id () != odb::id_oracle) + v.reset (db->query_one<V> ("age < 31")); + else + v.reset (db->query_one<V> ("\"age\" < 31")); + assert (v->count == 2); + } + + { + unique_ptr<V> v (db->query_one<V> (query::age < 31)); + assert (v->count == 2); + } + + t.commit (); +} + +template <typename V> +void +view4_test (const unique_ptr<database>& db) +{ + typedef odb::query<V> query; + typedef odb::result<V> result; + typedef typename result::iterator iterator; + + transaction t (db->begin ()); + + { + result r; + if (db->id () != odb::id_oracle) + r = db->query<V> ((query::person::age > 30) + "ORDER BY age"); + else + r = db->query<V> ((query::person::age > 30) + "ORDER BY \"age\""); + + iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "Joe" && i->last_name == "Dirt" && + i->name == "United States"); + + assert (++i != r.end ()); + assert (i->first_name == "Johan" && i->last_name == "Johansen" && + i->name == "Sweden"); + + assert (++i == r.end ()); + } + + { + result r (db->query<V> ( + (query::person::age > 30) + + "ORDER BY " + query::person::age)); + + iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "Joe" && i->last_name == "Dirt" && + i->name == "United States"); + + assert (++i != r.end ()); + assert (i->first_name == "Johan" && i->last_name == "Johansen" && + i->name == "Sweden"); + + assert (++i == r.end ()); + } + + { + result r (db->query<V> (query::residence::code == "US")); + + iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "Joe" && i->last_name == "Dirt" && + i->name == "United States"); + + assert (++i == r.end ()); + } + + t.commit (); +} + +template <typename V> +void +view6_test (const unique_ptr<database>& db, const odb::query<V>& q) +{ + typedef odb::result<V> result; + typedef typename result::iterator iterator; + + transaction t (db->begin ()); + + { + result r (db->query<V> (q)); + + iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "John" && i->last_name == "Doe" && + i->employer == "Simple Tech, Inc"); + + assert (++i != r.end ()); + assert (i->first_name == "Joe" && i->last_name == "Dirt" && + i->employer == "Simple Tech, Inc"); + + assert (++i == r.end ()); + } + + t.commit (); +} + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // + // + { + country ca ("CA", "Canada"); + country za ("ZA", "South Africa"); + country us ("US", "United States"); + country se ("SE", "Sweden"); + + person p1 (1, "John", "Doe", 30, male, measures (60, 160), &ca, &ca); + person p2 (2, "Jane", "Doe", 29, female, measures (70, 170), &za, &us); + person p3 (3, "Joe", "Dirt", 31, male, measures (80, 180), &us, &za); + person p4 (4, "Johan", "Johansen", 32, male, measures (90, 190), &se, + &se); + + p2.husband = &p1; + + employer st ("Simple Tech, Inc"); + employer ct ("Complex Tech, Inc"); + + p2.previous_employer = st.name; + p3.previous_employer = ct.name; + + st.employees.push_back (&p1); + st.employees.push_back (&p3); + st.head_count = 2; + + ct.employees.push_back (&p2); + ct.employees.push_back (&p4); + ct.head_count = 2; + + transaction t (db->begin ()); + db->persist (ca); + db->persist (za); + db->persist (us); + db->persist (se); + + db->persist (p1); + db->persist (p2); + db->persist (p3); + db->persist (p4); + + db->persist (st); + db->persist (ct); + t.commit (); + } + + // view1 + // + { + typedef odb::result<view1> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view1> ()); + assert (size (r) == 4); + } + + { + result r; + if (db->id () != odb::id_oracle) + r = db->query<view1> ("ORDER BY age"); + else + r = db->query<view1> ("ORDER BY \"age\""); + + assert (size (r) == 4); + } + + { + result r; + if (db->id () != odb::id_oracle) + r = db->query<view1> ("age < 31 ORDER BY age"); + else + r = db->query<view1> ("\"age\" < 31 ORDER BY \"age\""); + + view1_check (r); + } + + t.commit (); + } + } + + // view1a + // + { + typedef odb::result<view1a> result; + + { + transaction t (db->begin ()); + + result r (db->query<view1a> ()); + view1_check (r); + + t.commit (); + } + } + + // view1b + // + { + typedef odb::result<view1b> result; + + { + transaction t (db->begin ()); + + result r (db->query<view1b> ()); + view1_check (r); + + t.commit (); + } + + // No native parameter support in dynamic multi-database mode. + // +#ifndef MULTI_DATABASE + { + typedef odb::query<view1b> query; + + transaction t (db->begin ()); + +#ifndef DATABASE_ORACLE + result r (db->query<view1b> ("first = " + query::_val ("Jane"))); +#else + result r (db->query<view1b> ("\"first\" = " + query::_val ("Jane"))); +#endif + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first == "Jane" && i->last == "Doe"); + assert (++i == r.end ()); + + t.commit (); + } +#endif + } + + // view1c + // + { + typedef odb::result<view1c> result; + + { + transaction t (db->begin ()); + + result r; + if (db->id () != odb::id_oracle) + r = db->query<view1c> ("SELECT first, last, age " + "FROM t_view_b_person " + "WHERE age < 31 ORDER BY age"); + else + r = db->query<view1c> ("SELECT \"first\", \"last\", \"age\" " + "FROM \"t_view_b_person\" " + "WHERE \"age\" < 31 ORDER BY \"age\""); + view1_check (r); + + t.commit (); + } + } + + // view1d + // + { + typedef odb::result<view1d> result; + + { + transaction t (db->begin ()); + + { + result r; + if (db->id () != odb::id_oracle) + r = db->query<view1d> ("age < 31 ORDER BY age"); + else + r = db->query<view1d> ("\"age\" < 31 ORDER BY \"age\""); + + view1_check (r); + } + + t.commit (); + } + } + + // view2 + // + view2_test<view2> (db); + view2_test<view2a> (db); + view2_test<view2b> (db); + view2_test<view2c> (db); + + // view3 + // + { + typedef odb::result<const view3> result; // Test const result. + + { + transaction t (db->begin ()); + + { + result r (db->query<view3> ()); + + size_t count (0); + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + if (i->last_name == "Doe") + assert (i->count == 2); + else if (i->last_name == "Dirt" || + i->last_name == "Johansen") + assert (i->count == 1); + else + assert (false); + + count++; + } + + assert (count == 3); + } + + t.commit (); + } + } + + // view3a + // + { + typedef odb::query<view3a> query; + typedef odb::result<view3a> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view3a> (query::last_name == "Doe")); + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->last_name == "Doe" && i->count == 2); + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view4 + // + view4_test<view4> (db); + view4_test<view4a> (db); + + // view5 + // + { + typedef odb::query<view5> query; + typedef odb::result<view5> result; + + { + transaction t (db->begin ()); + + { + result r ( + db->query<view5> ( + query::residence::name == query::nationality::name)); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "John" && i->last_name == "Doe" && + i->rname == "Canada" && i->rname == "Canada"); + + assert (++i != r.end ()); + assert (i->first_name == "Johan" && i->last_name == "Johansen" && + i->rname == "Sweden" && i->rname == "Sweden"); + + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view6 + // + view6_test<view6> ( + db, odb::query<view6>::employer::name == "Simple Tech, Inc"); + + view6_test<view6a> ( + db, odb::query<view6a>::employer::name == "Simple Tech, Inc"); + + view6_test<view6b> ( + db, odb::query<view6b>::employer::name == "Simple Tech, Inc"); + + // No native parameter support in dynamic multi-database mode. + // +#ifndef MULTI_DATABASE + view6_test<view6c> ( +#ifndef DATABASE_ORACLE + db, "e.name = " + odb::query<view6c>::_val ("Simple Tech, Inc")); +#else + db, "\"e\".\"name\" = " + odb::query<view6c>::_val ("Simple Tech, Inc")); +#endif +#endif + + // view7 + // + { + typedef odb::query<view7> query; + typedef odb::result<view7> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view7> (query::person::last_name == "Doe")); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "Jane" && i->last_name == "Doe" && + !i->head_count.null () && *i->head_count == 2); + + assert (++i != r.end ()); + assert (i->first_name == "John" && i->last_name == "Doe" && + i->head_count.null ()); + + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view8 + // + { + typedef odb::result<view8> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view8> ()); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->wife_name == "Jane" && i->husb_name == "John"); + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view9 + // + { + typedef odb::query<view9> query; + typedef odb::result<view9> result; + + { + transaction t (db->begin ()); + + { + // Test case-insensitive clause prefix detection. + // + result r (db->query<view9> ("where" + (query::gender == female))); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->first_name == "Jane" && i->last_name == "Doe" && + i->gender == female); + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view10 + // + { + typedef odb::query<view10> query; + typedef odb::result<view10> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view10> ( + query::measures.weight > 60 && + query::measures.hight < 190)); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->last_name == "Doe" && + i->measures.weight == 70 && i->measures.hight == 170); + + assert (++i != r.end ()); + assert (i->last_name == "Dirt" && + i->measures.weight == 80 && i->measures.hight == 180); + + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view11 + // + { + typedef odb::result<view11> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view11> ()); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->last_name == "Doe" && i->hight == 170); + + assert (++i != r.end ()); + assert (i->last_name == "Dirt" && i->hight == 180); + + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view12 + // + { + typedef odb::query<view12> query; + typedef odb::result<view12> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view12> (query::last_name == "Dirt")); + + result::iterator i (r.begin ()); + + assert (i != r.end ()); + assert (i->residence == "US"); + assert (++i == r.end ()); + } + + t.commit (); + } + } + + // view13 + // + { + typedef odb::query<view13> query; + typedef odb::result<view13> result; + + { + transaction t (db->begin ()); + + { + result r (db->query<view13> ( + (query::person::age < 32) + + "ORDER BY" + query::employer::name)); + + assert (size (r) == 2); + } + + t.commit (); + } + } + + // view14 + // + { + transaction t (db->begin ()); + assert (size (db->query<view14> ()) == 2); + t.commit (); + } + + // Test join types. + // + { + using namespace test2; + + { + obj1 o11 (1, 1); + obj1 o12 (2, 2); + + obj2 o21 (1, 1); + obj2 o22 (2, 1); + obj2 o23 (3, 3); + + transaction t (db->begin ()); + db->persist (o11); + db->persist (o12); + db->persist (o21); + db->persist (o22); + db->persist (o23); + t.commit (); + } + + { + typedef odb::query<vleft> query; + typedef odb::result<vleft> result; + + transaction t (db->begin ()); + result r (db->query<vleft> ( + "ORDER BY" + query::o1::id1 + "," + query::o2::id2)); + result::iterator i (r.begin ()); + assert ( i != r.end () && i->id1 == 1 && *i->id2 == 1); + assert (++i != r.end () && i->id1 == 1 && *i->id2 == 2); + assert (++i != r.end () && i->id1 == 2 && i->id2.null ()); + assert (++i == r.end ()); + t.commit (); + } + +#if !defined(DATABASE_SQLITE) && !defined(MULTI_DATABASE) + { + typedef odb::query<vright> query; + typedef odb::result<vright> result; + + transaction t (db->begin ()); + result r (db->query<vright> ( + "ORDER BY" + query::o1::id1 + "," + query::o2::id2)); + result::iterator i (r.begin ()); + assert ( i != r.end () && i->id1 == 1 && *i->id2 == 1); + assert (++i != r.end () && i->id1 == 1 && *i->id2 == 2); + assert (++i != r.end () && i->id1 == 2 && i->id2.null ()); + assert (++i == r.end ()); + t.commit (); + } +#endif + +#if !defined(DATABASE_SQLITE) && \ + !defined(DATABASE_MYSQL) && \ + !defined(MULTI_DATABASE) + { + typedef odb::query<vfull> query; + typedef odb::result<vfull> result; + + transaction t (db->begin ()); + result r (db->query<vfull> ( + "ORDER BY" + query::o1::id1 + "," + query::o2::id2)); + result::iterator i (r.begin ()); + + // SQL Server orders NULL values first. Got to be different. + // +#ifdef DATABASE_MSSQL + assert ( i != r.end () && i->id1.null () && *i->id2 == 3); + assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 1); + assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 2); + assert (++i != r.end () && *i->id1 == 2 && i->id2.null ()); +#else + assert ( i != r.end () && *i->id1 == 1 && *i->id2 == 1); + assert (++i != r.end () && *i->id1 == 1 && *i->id2 == 2); + assert (++i != r.end () && *i->id1 == 2 && i->id2.null ()); + assert (++i != r.end () && i->id1.null () && *i->id2 == 3); +#endif + assert (++i == r.end ()); + t.commit (); + } +#endif + + { + typedef odb::query<vinner> query; + typedef odb::result<vinner> result; + + transaction t (db->begin ()); + result r (db->query<vinner> ( + "ORDER BY" + query::o1::id1 + "," + query::o2::id2)); + result::iterator i (r.begin ()); + assert ( i != r.end () && i->id1 == 1 && i->id2 == 1); + assert (++i != r.end () && i->id1 == 1 && i->id2 == 2); + assert (++i == r.end ()); + t.commit (); + } + + { + typedef odb::query<vcross> query; + typedef odb::result<vcross> result; + + transaction t (db->begin ()); + result r (db->query<vcross> ( + "ORDER BY" + query::o1::id1 + "," + query::o2::id2)); + result::iterator i (r.begin ()); + assert ( i != r.end () && i->id1 == 1 && i->id2 == 1); + assert (++i != r.end () && i->id1 == 1 && i->id2 == 2); + assert (++i != r.end () && i->id1 == 1 && i->id2 == 3); + assert (++i != r.end () && i->id1 == 2 && i->id2 == 1); + assert (++i != r.end () && i->id1 == 2 && i->id2 == 2); + assert (++i != r.end () && i->id1 == 2 && i->id2 == 3); + assert (++i == r.end ()); + t.commit (); + } + + // Inner JOIN via relationship/container. + // + { + obj3 o31 (1, 1); + obj3 o32 (2, 2); + + obj4 o41 (1, 1); + obj4 o42 (2, 2); + o42.o3.push_back (&o32); + + transaction t (db->begin ()); + db->persist (o31); + db->persist (o32); + db->persist (o41); + db->persist (o42); + t.commit (); + } + + { + typedef odb::result<vrel> result; + + transaction t (db->begin ()); + result r (db->query<vrel> ()); + result::iterator i (r.begin ()); + assert ( i != r.end () && i->id4 == 2); + assert (++i == r.end ()); + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/view/basics/test.hxx b/odb-tests/common/view/basics/test.hxx new file mode 100644 index 0000000..25c502b --- /dev/null +++ b/odb-tests/common/view/basics/test.hxx @@ -0,0 +1,616 @@ +// file : common/view/basics/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> +#include <cstddef> // std::size_t + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +struct employer; + +#pragma db object +struct country +{ + country (const std::string& c, const std::string& n) + : code (c), name (n) + { + } + + country () + { + } + + #pragma db id + std::string code; // ISO 2-letter country code. + + std::string name; +}; + +enum gender_type {male, female}; + +#pragma db value +struct measures +{ + measures (unsigned short w, unsigned short h) : weight (w), hight (h) {} + measures () {} + + unsigned short weight; + unsigned short hight; +}; + +#pragma db object +struct person +{ + typedef ::measures measures_type; + + person (unsigned long i, + const std::string& fn, + const std::string& ln, + unsigned short a, + gender_type g, + const measures_type m, + country* r, + country* n) + : id (i), + first_name_ (fn), + last_name_ (ln), + age (a), + gender (g), + measures (m), + residence (r), + nationality (n), + husband (0) + { + } + + person () + { + } + + #pragma db id + unsigned long id; + + #pragma db column("first") + std::string first_name_; + + #pragma db column("last") + std::string last_name_; + + unsigned short age; + + // #pragma db type("INT") - in MySQL test type pragma copying + gender_type gender; + + measures_type measures; + + #pragma db not_null + country* residence; + + #pragma db not_null + country* nationality; + + #pragma db inverse(employees) + employer* employed_by; + + // A non-pointer relationship. + // + odb::nullable<std::string> previous_employer; + + person* husband; // Self-reference. +}; + +#pragma db object +struct employer +{ + employer (const std::string& n) + : name (n) + { + } + + employer () + { + } + + #pragma db id + std::string name; + unsigned int head_count; + std::vector<person*> employees; +}; + +// +// General view with no associated objects. +// + +// Complete suffix query template. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view query("SELECT first, last, age FROM t_view_b_person") +#else +# pragma db view query("SELECT \"first\", \"last\", \"age\" " \ + "FROM \"t_view_b_person\"") +#endif +struct view1 +{ + std::string first; + std::string last; + unsigned short age; +}; + +// Complete query. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view query("SELECT first, last, age " \ + "FROM t_view_b_person " \ + "WHERE age < 31 ORDER BY age") +#else +# pragma db view query("SELECT \"first\", \"last\", \"age\" " \ + "FROM \"t_view_b_person\" " \ + "WHERE \"age\" < 31 ORDER BY \"age\"") +#endif +struct view1a +{ + std::string first; + std::string last; + unsigned short age; +}; + +// Complete placeholder query template. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view query("SELECT first, last, age " \ + "FROM t_view_b_person " \ + "WHERE age < 31 AND (?) ORDER BY age") +#else +# pragma db view query("SELECT \"first\", \"last\", \"age\" " \ + "FROM \"t_view_b_person\" " \ + "WHERE \"age\" < 31 AND (?) ORDER BY \"age\"") +#endif +struct view1b +{ + std::string first; + std::string last; + unsigned short age; +}; + +// Runtime query. +// +#pragma db view //query() +struct view1c +{ + std::string first; + std::string last; + unsigned short age; +}; + +// Assembled SELECT and FROM-lists. +// +#pragma db view table("t_view_b_person") +struct view1d +{ + #pragma db column("first") + std::string first; + + #pragma db column("last") + std::string last; + + #pragma db column("age") + unsigned short age; +}; + +// +// Count view plus associated object. +// + +// Complete suffix query. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view object(person) \ + query("SELECT count(id) FROM t_view_b_person") +#else +# pragma db view object(person) \ + query("SELECT count(\"id\") FROM \"t_view_b_person\"") +#endif +struct view2 +{ + std::size_t count; +}; + +// Generated query, literal column. +// +#pragma db view object(person) +struct view2a +{ +#ifndef ODB_DATABASE_ORACLE + #pragma db column("count(id)") +#else + #pragma db column("count(\"id\")") +#endif + std::size_t count; +}; + +// Generated query, qualified literal column. +// +#pragma db view object(person) +struct view2b +{ +#ifndef ODB_DATABASE_ORACLE + #pragma db column("count(t_view_b_person.id)") +#else + #pragma db column("count(\"t_view_b_person\".\"id\")") +#endif + std::size_t count; +}; + +// Generated query, expression column. +// +#pragma db view object(person) +struct view2c +{ + #pragma db column("count(" + person::id + ")") + std::size_t count; +}; + +// +// Aggregate view plus associated object with a custom alias. +// + +// Complete suffix query. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view object(person = test) \ + query("SELECT last, count(last) " \ + "FROM t_view_b_person " \ + "GROUP BY last") +#else +# pragma db view object(person = test) \ + query("SELECT \"last\", count(\"last\") " \ + "FROM \"t_view_b_person\" " \ + "GROUP BY \"last\"") +#endif +struct view3 +{ + std::string last_name; + std::size_t count; +}; + +// Generated query with integrated query condition and placeholder. +// +#pragma db view object(person = test) \ + query((?) + "GROUP BY" + test::last_name_) +struct view3a +{ + // Automatically resolved to test::last_name_. + // + std::string last_name; + + #pragma db column("count(" + test::last_name_ + ")") + std::size_t count; +}; + +// +// JOIN view plus associated objects, some with custom aliases. +// + +// Complete suffix query. +// +#ifndef ODB_DATABASE_ORACLE +# pragma db view object(person) object(country = residence) \ + query("SELECT first, last, residence.name " \ + "FROM t_view_b_person " \ + "LEFT JOIN t_view_b_country AS residence " \ + "ON t_view_b_person.residence = residence.code") +#else +# pragma db view object(person) object(country = residence) \ + query("SELECT \"first\", \"last\", \"residence\".\"name\" " \ + "FROM \"t_view_b_person\" " \ + "LEFT JOIN \"t_view_b_country\" \"residence\" " \ + "ON \"t_view_b_person\".\"residence\" = \"residence\".\"code\"") +#endif +struct view4 +{ + std::string first_name; + std::string last_name; + std::string name; +}; + +// Generated query. +// +#pragma db view object(person) \ + object(country = residence: person::residence) +struct view4a +{ + std::string first_name; + std::string last_name; + std::string name; +}; + +// +// JOIN the same object twice. +// +#pragma db view object(person) \ + object(country = residence: person::residence) \ + object(country = nationality: person::nationality) \ + query((?) + "ORDER BY" + person::age) +struct view5 +{ + std::string first_name; + std::string last_name; + + #pragma db column(residence::name) + std::string rname; + + #pragma db column(nationality::name) + std::string nname; +}; + +// +// JOIN via one(i)-to-many relationship. +// + +// Automatic relationship discovery. +// +#pragma db view object(person) object(employer) +struct view6 +{ + std::string first_name; + std::string last_name; + + #pragma db column(::employer::name) + std::string employer; +}; + +// Manual relationship specification, left side. +// +#pragma db view object(person) object(employer: person::employed_by) +struct view6a +{ + std::string first_name; + std::string last_name; + + #pragma db column(::employer::name) + std::string employer; +}; + +// Manual relationship specification, right side. +// +#pragma db view object(person) object(employer: employer::employees) +struct view6b +{ + std::string first_name; + std::string last_name; + + #pragma db column(::employer::name) + std::string employer; +}; + +// The same using tables. +// +#if defined(ODB_DATABASE_ORACLE) +#pragma db view table("t_view_b_person" = "p") \ + table("t_view_b_employer_employees" = "ee": "\"ee\".\"value\" = \"p\".\"id\"")\ + table("t_view_b_employer" = "e": "\"ee\".\"object_id\" = \"e\".\"name\"") +#elif defined(ODB_DATABASE_MSSQL) +#pragma db view table("t_view_b_person" = "p") \ + table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \ + table("t_view_b_employer" = "e": "[ee].[object_id] = e.name") +#elif defined(ODB_DATABASE_MYSQL) +#pragma db view table("t_view_b_person" = "p") \ + table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \ + table("t_view_b_employer" = "e": "`ee`.`object_id` = e.name") +#else +#pragma db view table("t_view_b_person" = "p") \ + table("t_view_b_employer_employees" = "ee": "ee.value = p.id") \ + table("t_view_b_employer" = "e": "\"ee\".\"object_id\" = e.name") +#endif +struct view6c +{ + #pragma db column("p.first") + std::string first_name; + + #pragma db column("p.last") + std::string last_name; + + #pragma db column("e"."name") + std::string employer; +}; + +// +// JOIN via a custom condition. +// +#pragma db view object(person) \ + object(employer: person::previous_employer == employer::name)\ + query((?) + "ORDER BY" + person::age) +struct view7 +{ + std::string first_name; + std::string last_name; + + odb::nullable<unsigned int> head_count; +}; + +// +// Self-JOIN. +// +#pragma db view object(person = wife) object(person = husb) \ + query (wife::husband.is_not_null ()) +struct view8 +{ + #pragma db column(wife::first_name_) + std::string wife_name; + + #pragma db column(husb::first_name_) + std::string husb_name; +}; + +// +// Enum mapping. +// +#pragma db view object(person) +struct view9 +{ + std::string first_name; + std::string last_name; + gender_type gender; +}; + +// +// Composite in view. +// +#pragma db view object(person) query((?) + "ORDER BY" + person::age) +struct view10 +{ + std::string last_name; + ::measures measures; +}; + +// +// Composite in object. +// +#pragma db view object(person) \ + query((person::measures.weight > 60 && person::measures.hight < 190 && (?)) \ + + "ORDER BY" + person::age) +struct view11 +{ + std::string last_name; + + #pragma db column(person::measures.hight) + unsigned short hight; +}; + +// +// Extract object pointer as object id. +// +#pragma db view object(person) +struct view12 +{ + std::string residence; +}; + +// +// Test 'distinct' result modifier. +// +#pragma db view object(employer) object(person) query(distinct) +struct view13 +{ + std::string name; +}; + +// +// Test 'for_update' result modifier. +// +#pragma db view object(employer) query((?), for_update) +struct view14 +{ + std::string name; +}; + +// Test join types. +// +#pragma db namespace table("t2_") +namespace test2 +{ + #pragma db object + struct obj1 + { + obj1 (int id = 0, int n_ = 0): id1 (id), n (n_) {} + + #pragma db id + int id1; + + int n; + }; + + #pragma db object no_id + struct obj2 + { + obj2 (int id = 0, int n_ = 0): id2 (id), n (n_) {} + + #pragma db id + int id2; + + int n; + }; + + #pragma db view object(obj1 = o1) object(obj2 = o2 left: o1::n == o2::n) + struct vleft + { + int id1; + odb::nullable<int> id2; + }; + +#if !defined(ODB_DATABASE_SQLITE) && !defined(MULTI_DATABASE) + + #pragma db view object(obj2 = o2) object(obj1 = o1 right: o2::n == o1::n) + struct vright + { + int id1; + odb::nullable<int> id2; + }; + +#endif + +#if !defined(ODB_DATABASE_MYSQL) && \ + !defined(ODB_DATABASE_SQLITE) && \ + !defined(MULTI_DATABASE) + + #pragma db view object(obj1 = o1) object(obj2 = o2 full: o1::n == o2::n) + struct vfull + { + odb::nullable<int> id1; + odb::nullable<int> id2; + }; + +#endif + + #pragma db view object(obj1 = o1) object(obj2 = o2 inner: o1::n == o2::n) + struct vinner + { + int id1; + int id2; + }; + + #pragma db view object(obj1 = o1) object(obj2 = o2 cross) + struct vcross + { + int id1; + int id2; + }; + + // Inner JOIN via relationship/container. + // + #pragma db object + struct obj3 + { + obj3 (int id = 0, int n_ = 0): id3 (id), n (n_) {} + + #pragma db id + int id3; + + int n; + }; + + #pragma db object no_id + struct obj4 + { + obj4 (int id = 0, int n_ = 0): id4 (id), n (n_) {} + + #pragma db id + int id4; + + int n; + std::vector<obj3*> o3; + }; + + #pragma db view object(obj4) object(obj3 inner) + struct vrel + { + int id4; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/view/basics/testscript b/odb-tests/common/view/basics/testscript new file mode 100644 index 0000000..7c69c0c --- /dev/null +++ b/odb-tests/common/view/basics/testscript @@ -0,0 +1,53 @@ +# file : common/view/basics/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/view/olv/.gitignore b/odb-tests/common/view/olv/.gitignore new file mode 100644 index 0000000..2b95165 --- /dev/null +++ b/odb-tests/common/view/olv/.gitignore @@ -0,0 +1,46 @@ +# ODB-generated files. +# +test1-odb.?xx +test1-odb-*.?xx +test1.sql +test1-*.sql + +test2-odb.?xx +test2-odb-*.?xx +test2.sql +test2-*.sql + +test3-odb.?xx +test3-odb-*.?xx +test3.sql +test3-*.sql + +test4-odb.?xx +test4-odb-*.?xx +test4.sql +test4-*.sql + +test5-odb.?xx +test5-odb-*.?xx +test5.sql +test5-*.sql + +test6-odb.?xx +test6-odb-*.?xx +test6.sql +test6-*.sql + +test7-odb.?xx +test7-odb-*.?xx +test7.sql +test7-*.sql + +test8-odb.?xx +test8-odb-*.?xx +test8.sql +test8-*.sql + +test9-odb.?xx +test9-odb-*.?xx +test9.sql +test9-*.sql diff --git a/odb-tests/common/view/olv/buildfile b/odb-tests/common/view/olv/buildfile new file mode 100644 index 0000000..1edf0b4 --- /dev/null +++ b/odb-tests/common/view/olv/buildfile @@ -0,0 +1,50 @@ +# file : common/view/olv/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +hdrs = test1 test2 test3 test4 test5 test6 test7 test8 test9 + +exe{driver}: {hxx cxx}{* -*-odb -*-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 + +for h: $hdrs +{ + exe{driver}: {hxx ixx cxx}{$h-odb} + + <{hxx ixx cxx}{$h-odb}>: hxx{$h} libue{test-meta} + + for db: $databases + { + exe{driver}: {hxx ixx cxx}{$h-odb-$db}: include = $multi + <{hxx ixx cxx}{$h-odb-$db}>: hxx{$h} 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 t_view_olv_ \ + --generate-schema \ + --generate-query + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../../alias{database-client}: include = adhoc + +testscript@./: schemas = $hdrs diff --git a/odb-tests/common/view/olv/driver.cxx b/odb-tests/common/view/olv/driver.cxx new file mode 100644 index 0000000..77cf76e --- /dev/null +++ b/odb-tests/common/view/olv/driver.cxx @@ -0,0 +1,646 @@ +// file : common/view/olv/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test object loading views. +// + +#include <memory> // std::unique_ptr +#include <iostream> +#include <typeinfo> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test1.hxx" +#include "test2.hxx" +#include "test3.hxx" +#include "test4.hxx" +#include "test5.hxx" +#include "test6.hxx" +#include "test7.hxx" +#include "test8.hxx" +#include "test9.hxx" + +#include "test1-odb.hxx" +#include "test2-odb.hxx" +#include "test3-odb.hxx" +#include "test4-odb.hxx" +#include "test5-odb.hxx" +#include "test6-odb.hxx" +#include "test7-odb.hxx" +#include "test8-odb.hxx" +#include "test9-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic object loading functionality. + // + { + using namespace test1; + + { + object1 o1a (1, 123); + object2 o2 (1, "abc"); + + transaction t (db->begin ()); + db->persist (o1a); + db->persist (o2); + t.commit (); + } + + { + typedef odb::query<view1> query; + + transaction t (db->begin ()); + view1 v (db->query_value<view1> (query::object1::n == 123)); + assert (v.o2->s == "abc"); + t.commit (); + } + + { + transaction t (db->begin ()); + view2 v (db->query_value<view2> ()); + assert (v.o1->n == 123 && v.o2->s == "abc"); + t.commit (); + } + + { + transaction t (db->begin ()); + view3 v (db->query_value<view3> ()); + assert (v.o1->n == 123 && v.o2->s == "abc"); + t.commit (); + } + + { + transaction t (db->begin ()); + view4 v (db->query_value<view4> ()); + assert (v.s == "abc" && v.o2->s == "abc" && v.id == 1 && + v.o1->n == 123 && v.n == 123); + t.commit (); + } + + { + transaction t (db->begin ()); + view4 v (db->query_value<view4> ()); + assert (v.s == "abc" && v.o2->s == "abc" && v.id == 1 && + v.o1->n == 123 && v.n == 123); + t.commit (); + } + + { + typedef odb::query<view5> query; + + object1 o1b (123, 1); + + transaction t (db->begin ()); + db->persist (o1b); + view5 v (db->query_value<view5> (query::o1b::n == 1)); + assert (v.o1a->n == 123 && v.o2->s == "abc" && v.o1b->n == 1); + t.commit (); + } + } + + // Test loading of object pointers inside objects. + // + { + using namespace test2; + + shared_ptr<object1> o1 (new object1 (123)); + shared_ptr<object2> o2 (new object2 ("abc", o1)); + + { + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + view1 v (db->query_value<view1> ()); + assert (v.o2->s == "abc" && v.o2->o1->n == 123); + t.commit (); + } + + { + // Check session interaction. + // + transaction t (db->begin ()); + session s; + shared_ptr<object2> o2a (db->load<object2> (o2->id)); + view1 v (db->query_value<view1> ()); + assert (v.o2 == o2a); + t.commit (); + } + + { + transaction t (db->begin ()); + session s; + view2 v (db->query_value<view2> ()); + assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1); + t.commit (); + } + + shared_ptr<object3> o3 (new object3 (o2)); + + { + transaction t (db->begin ()); + db->persist (o3); + t.commit (); + } + + { + transaction t (db->begin ()); + session s; + view3 v (db->query_value<view3> ()); + assert (v.o1->n == 123 && v.o3->o2->s == "abc" && + v.o3->o2->o1 == v.o1); + t.commit (); + } + + shared_ptr<object1> o1b (new object1 (234)); + shared_ptr<object2> o2b (new object2 ("bcd", o1b)); + shared_ptr<object4> o4 (new object4); + o4->o2.push_back (o2); + o4->o2.push_back (o2b); + + { + transaction t (db->begin ()); + db->persist (o1b); + db->persist (o2b); + db->persist (o4); + t.commit (); + } + + { + transaction t (db->begin ()); + view4 v (db->query_value<view4> ()); + assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 && + v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234); + t.commit (); + } + + { + typedef odb::query<view5> query; + typedef odb::result<view5> result; + + transaction t (db->begin ()); + session s; + result r (db->query<view5> ("ORDER BY" + query::object1::id)); + result::iterator i (r.begin ()); + + assert (i != r.end ()); + { + const view5& v (*i); + + assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 && + v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234 && + v.o4->o2[0]->o1 == v.o1); + } + assert (++i != r.end ()); + { + const view5& v (*i); + + assert (v.o4->o2[0]->s == "abc" && v.o4->o2[0]->o1->n == 123 && + v.o4->o2[1]->s == "bcd" && v.o4->o2[1]->o1->n == 234 && + v.o4->o2[1]->o1 == v.o1); + } + assert (++i == r.end ()); + t.commit (); + } + + shared_ptr<object5> o5 (new object5 (o1b, o2)); + + { + transaction t (db->begin ()); + db->persist (o5); + t.commit (); + } + + { + transaction t (db->begin ()); + view6 v (db->query_value<view6> ()); + assert (v.o1a->n == 123 && v.o1b->n == 234); + t.commit (); + } + } + + // Test JOINs for pointed-to objects, existing and automatically added. + // + { + using namespace test3; + + shared_ptr<object1> o1 (new object1 (123)); + shared_ptr<object2> o2 (new object2 ("abc")); + + o1->o2 = o2; + o2->o1 = o1; + + { + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + view1a v (db->query_value<view1a> ()); + // VC11 + assert (v.o1->n == 123 && v.o1->o2.object_id<object2> () == o2->id); + t.commit (); + } + + { + transaction t (db->begin ()); + view1b v (db->query_value<view1b> ()); + // VC11 + assert (v.o1->n == 123 && v.o1->o2.object_id<object2> () == o2->id); + t.commit (); + } + + // Container case. + // + + shared_ptr<object3> o3 (new object3 (123)); + shared_ptr<object4> o4 (new object4 ("abc")); + + o3->o4 = o4; + o4->o3.push_back (o3); + + { + + transaction t (db->begin ()); + db->persist (o3); + db->persist (o4); + t.commit (); + } + + { + transaction t (db->begin ()); + view2a v (db->query_value<view2a> ()); + // VC11 + assert (v.o3->n == 123 && v.o3->o4.object_id<object4> () == o4->id); + t.commit (); + } + + { + transaction t (db->begin ()); + view2b v (db->query_value<view2b> ()); + // VC11 + assert (v.o3->n == 123 && v.o3->o4.object_id<object4> () == o4->id); + t.commit (); + } + } + + // Test by-value load. + // + { + using namespace test4; + + { + object1 o1 (1, 123); + object2 o2 (1, "abc", &o1); + + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + view1 v (db->query_value<view1> ()); + assert (v.o1.n == 123); + t.commit (); + } + + { + transaction t (db->begin ()); + view1a v (db->query_value<view1a> ()); + assert (!v.o1_null && v.o1.n == 123); + t.commit (); + } + + { + transaction t (db->begin ()); + view1b v (db->query_value<view1b> ()); + assert (/*v.o1_p == &v.o1 && */ v.o1.n == 123); // Copy ctor. + t.commit (); + } + + { + typedef odb::result<view1c> result; + + transaction t (db->begin ()); + result r (db->query<view1c> ()); + result::iterator i (r.begin ()); + assert (i != r.end ()); + + object1 o1; + view1c v (o1); + i.load (v); + + assert (v.o1_p == &o1 && o1.n == 123); + + assert (++i == r.end ()); + t.commit (); + } + + { + transaction t (db->begin ()); + session s; + view2 v (db->query_value<view2> ()); + assert (v.o1.n == 123 && v.o2.s == "abc" && v.o2.o1 == &v.o1); + t.commit (); + } + + object1 o1b (2, 234); + + { + transaction t (db->begin ()); + db->persist (o1b); + t.commit (); + } + + { + typedef odb::query<view2a> query; + + transaction t (db->begin ()); + session s; + view2a v (db->query_value<view2a> (query::object1::id == 2)); + assert (v.o1.n == 234 && v.o2_null); + t.commit (); + } + + shared_ptr<object3> o3 (new object3 (1, 123)); + + { + transaction t (db->begin ()); + db->persist (o3); + t.commit (); + } + + { + transaction t (db->begin ()); + { + view3 v (db->query_value<view3> ()); + assert (v.o3_p == &v.o3 && v.o3.n == 123); // Load into value. + } + session s; // Load into cache. + shared_ptr<object3> o3a (db->load<object3> (o3->id)); + { + view3 v (db->query_value<view3> ()); + assert (v.o3_p == o3a.get ()); // Load from cache. + } + t.commit (); + } + } + + // Test NULL object pointers. + // + { + using namespace test5; + + shared_ptr<object1> o1a (new object1 (123)); + shared_ptr<object1> o1b (new object1 (234)); + shared_ptr<object2> o2 (new object2 ("abc", o1a)); + + { + transaction t (db->begin ()); + db->persist (o1a); + db->persist (o1b); + db->persist (o2); + t.commit (); + } + + { + typedef odb::query<view1> query; + typedef odb::result<view1> result; + + transaction t (db->begin ()); + session s; + result r (db->query<view1> ("ORDER BY" + query::object1::id)); + result::iterator i (r.begin ()); + + assert (i != r.end ()); + { + const view1& v (*i); + assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1); + } + assert (++i != r.end ()); + { + const view1& v (*i); + assert (v.o1->n == 234 && !v.o2); + } + assert (++i == r.end ()); + t.commit (); + } + + shared_ptr<object3> o3a (new object3 (make_pair (1, 1), 123)); + shared_ptr<object3> o3b (new object3 (make_pair (2, 2), 234)); + shared_ptr<object4> o4 (new object4 ("abc", o3a)); + + { + transaction t (db->begin ()); + db->persist (o3a); + db->persist (o3b); + db->persist (o4); + t.commit (); + } + + { + typedef odb::query<view2> query; + typedef odb::result<view2> result; + + transaction t (db->begin ()); + session s; + result r (db->query<view2> ("ORDER BY" + query::object3::n)); + result::iterator i (r.begin ()); + + assert (i != r.end ()); + { + const view2& v (*i); + assert (v.o3->n == 123 && v.o4->s == "abc" && v.o4->o3 == v.o3); + } + assert (++i != r.end ()); + { + const view2& v (*i); + assert (v.o3->n == 234 && !v.o4); + } + assert (++i == r.end ()); + t.commit (); + } + } + + // Test interaction with sections. + // + { + using namespace test6; + + shared_ptr<object1> o1 (new object1 (123)); + shared_ptr<object2> o2 (new object2 ("abc", o1)); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + view1 v (db->query_value<view1> ()); + + assert (v.o1->n == 123 && v.o2->s == "abc" && + !v.o2->r.loaded () && !v.o2->o1); + + db->load (*v.o2, v.o2->r); + assert (v.o2->r.loaded () && v.o2->o1 && v.o2->o1->n == 123); + + t.commit (); + } + } + + // Test explicit conversion to smart pointer member. + // + { + using namespace test7; + + object1 o1 (123); + object2 o2 ("abc", &o1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + session s; + view1 v (db->query_value<view1> ()); + assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1.get ()); + t.commit (); + } + } + + // Test loading objects without id. + // + { + using namespace test8; + + object1 o1 (123); + object2 o2 ("abc", &o1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + session s; + view1 v (db->query_value<view1> ()); + assert (v.o1->n == 123 && v.o2->s == "abc" && v.o2->o1 == v.o1.get ()); + t.commit (); + } + } + + // Test loading polymorphic objects. + // + { + using namespace test9; + + root r (1); + base b (2, "a"); + derived d (3, "b", true); + + { + transaction t (db->begin ()); + db->persist (r); + db->persist (b); + db->persist (d); + t.commit (); + } + + { + transaction t (db->begin ()); + + // Load via root. + // + { + view1r r (db->query_value<view1r> (query<view1r>::n == 1)); + auto& o (*r.o); + assert (r.n == 1 && r.o->n == 1 && typeid (o) == typeid (root)); + } + + { + view1r r (db->query_value<view1r> (query<view1r>::n == 2)); + auto& o (*r.o); + assert (r.n == 2 && r.o->n == 2 && typeid (o) == typeid (base)); + base& b (dynamic_cast<base&> (*r.o)); + assert (b.s == "a"); + } + + { + view1r r (db->query_value<view1r> (query<view1r>::n == 3)); + auto& o (*r.o); + assert (r.n == 3 && r.o->n == 3 && typeid (o) == typeid (derived)); + derived& d (dynamic_cast<derived&> (o)); + assert (d.s == "b" && d.b); + } + + // Load via base. + // + { + view1b r (db->query_value<view1b> (query<view1b>::n == 2)); + assert (r.s == "a" && r.n == 2 && r.o->n == 2 && b.s == "a"); + } + + { + view1b r (db->query_value<view1b> (query<view1b>::n == 3)); + auto& o (*r.o); + assert (r.s == "b" && r.n == 3 && r.o->n == 3 && + typeid (o) == typeid (derived)); + derived& d (dynamic_cast<derived&> (o)); + assert (d.s == "b" && d.b); + } + + // Load via derived. + // + { + view1d r (db->query_value<view1d> ()); + assert (r.s == "b" && r.n == 3 && + r.o->n == 3 && r.o->s == "b" && r.o->b); + } + + t.commit (); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/view/olv/test1.hxx b/odb-tests/common/view/olv/test1.hxx new file mode 100644 index 0000000..0de9483 --- /dev/null +++ b/odb-tests/common/view/olv/test1.hxx @@ -0,0 +1,116 @@ +// file : common/view/olv/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#include <string> +#include <memory> // unique_ptr +#include <utility> // std::move + +#include <odb/core.hxx> + +// Test basic object loading functionality. +// +#pragma db namespace table("t1_") pointer(std::unique_ptr) +namespace test1 +{ + #pragma db object + struct object1 + { + object1 (int id_ = 0, int n_ = 0): id (id_), n (n_) {} + + #pragma db id + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 (int id_ = 0, const char* s_ = ""): id (id_), s (s_) {} + + #pragma db id + int id; + + std::string s; + }; + + #pragma db view object(object1) object(object2: object1::id == object2::id) + struct view1 + { + // VC12 workaround (no default move constructor generation). + // + view1 () {} + view1 (view1&& x): o2 (std::move (x.o2)) {} + + std::unique_ptr<object2> o2; + }; + + #pragma db view object(object1) object(object2: object1::id == object2::id) + struct view2 + { + // VC12 workaround (no default move constructor generation). + // + view2 () {} + view2 (view2&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {} + + std::unique_ptr<object2> o2; + std::unique_ptr<object1> o1; + }; + + #pragma db view object(object1 = o1) object(object2 = o2: o1::id == o2::id) + struct view3 + { + // VC12 workaround (no default move constructor generation). + // + view3 () {} + view3 (view3&& x): o1 (std::move (x.o1)), o2 (std::move (x.o2)) {} + + std::unique_ptr<object1> o1; + std::unique_ptr<object2> o2; + }; + + #pragma db view object(object1 = o1) object(object2 = o2: o1::id == o2::id) + struct view4 + { + // VC12 workaround (no default move constructor generation). + // + view4 () {} + view4 (view4&& x): s (std::move (x.s)), + o2 (std::move (x.o2)), + id (x.id), + o1 (std::move (x.o1)), + n (x.n) {} + + std::string s; + std::unique_ptr<object2> o2; + + #pragma db column(o1::id) + int id; + + std::unique_ptr<object1> o1; + int n; + }; + + #pragma db view \ + object(object1) \ + object(object2: object1::id == object2::id) \ + object(object1 = o1b: object1::id == o1b::n) + struct view5 + { + // VC12 workaround (no default move constructor generation). + // + view5 () {} + view5 (view5&& x): o1a (std::move (x.o1a)), + o2 (std::move (x.o2)), + o1b (std::move (x.o1b)) {} + + std::unique_ptr<object1> o1a; + std::unique_ptr<object2> o2; + std::unique_ptr<object1> o1b; + }; +} + +#endif // TEST1_HXX diff --git a/odb-tests/common/view/olv/test2.hxx b/odb-tests/common/view/olv/test2.hxx new file mode 100644 index 0000000..a769daa --- /dev/null +++ b/odb-tests/common/view/olv/test2.hxx @@ -0,0 +1,122 @@ +// file : common/view/olv/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#include <string> +#include <vector> +#include <memory> // shared_ptr + +#include <odb/core.hxx> + +// Test loading of object pointers inside objects. +// +#pragma db namespace table("t2_") pointer(std::shared_ptr) session +namespace test2 +{ + using std::shared_ptr; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {} + + #pragma db id auto + int id; + + std::string s; + shared_ptr<object1> o1; + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + shared_ptr<object2> o2; + }; + + #pragma db view object(object1) object(object2) + struct view2 + { + shared_ptr<object2> o2; // "Unfortunate" order. + shared_ptr<object1> o1; + }; + + #pragma db object + struct object3 + { + object3 () {} + object3 (shared_ptr<object2> o2_): o2 (o2_) {} + + #pragma db id auto + int id; + + shared_ptr<object2> o2; + }; + + #pragma db view object(object1) object(object2) object(object3) + struct view3 + { + shared_ptr<object3> o3; // "Unfortunate" order. + shared_ptr<object1> o1; + }; + + #pragma db object + struct object4 + { + #pragma db id auto + int id; + + std::vector<shared_ptr<object2>> o2; + }; + + #pragma db view object(object4) + struct view4 + { + shared_ptr<object4> o4; + }; + + #pragma db view object(object4) object (object2) object(object1) + struct view5 + { + shared_ptr<object4> o4; // "Unfortunate" order. + shared_ptr<object1> o1; + }; + + #pragma db object + struct object5 + { + object5 () {} + object5 (shared_ptr<object1> o1_, shared_ptr<object2> o2_) + : o1 (o1_), o2 (o2_) {} + + #pragma db id auto + int id; + + shared_ptr<object1> o1; + shared_ptr<object2> o2; + }; + + #pragma db view object(object5) object (object2) \ + object(object1 = o1a: object2::o1) \ + object(object1 = o1b: object5::o1) + struct view6 + { + shared_ptr<object1> o1a; + shared_ptr<object1> o1b; + }; +} + +#endif // TEST2_HXX diff --git a/odb-tests/common/view/olv/test3.hxx b/odb-tests/common/view/olv/test3.hxx new file mode 100644 index 0000000..8cf4344 --- /dev/null +++ b/odb-tests/common/view/olv/test3.hxx @@ -0,0 +1,106 @@ +// file : common/view/olv/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#include <string> +#include <vector> +#include <memory> // shared_ptr + +#include <odb/core.hxx> +#include <odb/lazy-ptr.hxx> + +// Test JOINs for pointed-to objects, existing and automatically added. +// +#pragma db namespace table("t3_") pointer(std::shared_ptr) session +namespace test3 +{ + using std::shared_ptr; + + struct object2; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + + #pragma db inverse(o1) + odb::lazy_weak_ptr<object2> o2; + }; + + #pragma db object + struct object2 + { + object2 (const char* s_ = ""): s (s_) {} + + #pragma db id auto + int id; + + std::string s; + + shared_ptr<object1> o1; + }; + + #pragma db view object(object1) object(object2) + struct view1a // Existing JOIN. + { + shared_ptr<object1> o1; + }; + + #pragma db view object(object1) + struct view1b // Automatic JOIN. + { + shared_ptr<object1> o1; + }; + + // Container case. + // + struct object4; + + #pragma db object + struct object3 + { + object3 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + + #pragma db inverse(o3) + odb::lazy_weak_ptr<object4> o4; + }; + + #pragma db object + struct object4 + { + object4 (const char* s_ = ""): s (s_) {} + + #pragma db id auto + int id; + + std::string s; + + std::vector<shared_ptr<object3>> o3; + }; + + #pragma db view object(object3) object(object4 = o4) + struct view2a // Existing JOIN. + { + shared_ptr<object3> o3; + }; + + #pragma db view object(object3) + struct view2b // Automatic JOIN. + { + shared_ptr<object3> o3; + }; +} + +#endif // TEST3_HXX diff --git a/odb-tests/common/view/olv/test4.hxx b/odb-tests/common/view/olv/test4.hxx new file mode 100644 index 0000000..4d8b804 --- /dev/null +++ b/odb-tests/common/view/olv/test4.hxx @@ -0,0 +1,174 @@ +// file : common/view/olv/test4.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST4_HXX +#define TEST4_HXX + +#include <string> +#include <memory> // shared_ptr +#include <cassert> + +#include <odb/core.hxx> + +// Test by-value load. +// +#pragma db namespace table("t4_") session +namespace test4 +{ + #pragma db object + struct object1 + { + object1 (int id_ = 0, int n_ = 0): id (id_), n (n_) {} + + #pragma db id + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 (int id_ = 0, const char* s_ = "", object1* o1_ = 0) + : id (id_), s (s_), o1 (o1_) {} + + #pragma db id + int id; + + std::string s; + object1* o1; // Shallow copy. + }; + + typedef object1* object1_ptr; + typedef object2* object2_ptr; + + #pragma db view object(object1) + struct view1 + { + #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set() + + #pragma db transient + object1 o1; + }; + + #pragma db view object(object1) transient + struct view1a + { + view1a (): o1_null (true) {} + + #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) \ + set(this.o1_null = !(?)) + + object1 o1; + bool o1_null; + }; + + #pragma db view object(object1) + struct view1b + { + view1b (): o1_p (0) {} + + #pragma db transient + object1 o1; + + #pragma db get(&this.o1) set(o1_p = (?)) + object1* o1_p; + }; + + #pragma db view object(object1) + struct view1c + { + view1c (object1& o1): o1_p (&o1) {} + + object1* o1_p; + }; + + #pragma db view object(object1) object(object2) transient + struct view2 + { + #pragma db member(o2_) virtual(object2_ptr) get(&this.o2) set() + #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set() + + object1 o1; + object2 o2; + + view2 () {} + view2 (const view2& v) + : o1 (v.o1), o2 (v.o2) + { + if (v.o2.o1 != 0) + { + assert (v.o2.o1 == &v.o1); + o2.o1 = &o1; + } + } + }; + + #pragma db view object(object1) object(object2) transient + struct view2a + { + #pragma db member(o2_) virtual(object2_ptr) get(&this.o2) \ + set(o2_null = !(?)) + #pragma db member(o1_) virtual(object1_ptr) get(&this.o1) set() + + object1 o1; + object2 o2; + bool o2_null; + + view2a () {} + view2a (const view2a& v) + : o1 (v.o1), o2 (v.o2) + { + if (v.o2.o1 != 0) + { + assert (v.o2.o1 == &v.o1); + o2.o1 = &o1; + } + } + }; + + // Test loading into raw pointer with non-raw object pointer. + // + using std::shared_ptr; + + #pragma db object pointer(shared_ptr) + struct object3 + { + object3 (int id_ = 0, int n_ = 0): id (id_), n (n_) {} + + #pragma db id + int id; + + int n; + }; + + #pragma db view object(object3) + struct view3 + { + // This view implements the following slightly twisted logic: if the + // object is already in the cache, then set o3_p to that. Otherwise, + // load it into the by-value instance. We can also check whether o3_p + // points to o3 to distinguish between the two outcomes. + // + + // Since we may be getting the pointer as both smart and raw, we + // need to create a bit of support code to use in the modifier + // expression. + // + void set_o3 (object3* p) {o3_p = p;} // &o3 or NULL. + void set_o3 (shared_ptr<object3> p) {o3_p = p.get ();} // From cache. + + #pragma db get(&this.o3) set(set_o3(?)) + object3* o3_p; + + #pragma db transient + object3 o3; + + // Return-by-value support (query_value()). + // + view3 (): o3_p (0) {} + view3 (const view3& x): o3_p (x.o3_p == &x.o3 ? &o3 : x.o3_p), o3 (x.o3) {} + }; +} + +#endif // TEST4_HXX diff --git a/odb-tests/common/view/olv/test5.hxx b/odb-tests/common/view/olv/test5.hxx new file mode 100644 index 0000000..e3a671b --- /dev/null +++ b/odb-tests/common/view/olv/test5.hxx @@ -0,0 +1,86 @@ +// file : common/view/olv/test5.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST5_HXX +#define TEST5_HXX + +#include <string> +#include <memory> // shared_ptr +#include <utility> // pair + +#include <odb/core.hxx> + +// Test NULL object pointers. +// +#pragma db namespace table("t5_") pointer(std::shared_ptr) session +namespace test5 +{ + using std::shared_ptr; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {} + + #pragma db id auto + int id; + + std::string s; + shared_ptr<object1> o1; + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + shared_ptr<object1> o1; + shared_ptr<object2> o2; + }; + + typedef std::pair<int, int> comp_id; + #pragma db value(comp_id) + + #pragma db object + struct object3 + { + object3 (comp_id id_ = comp_id (), int n_ = 0): id (id_), n (n_) {} + + #pragma db id + comp_id id; + + int n; + }; + + #pragma db object + struct object4 + { + object4 () {} + object4 (const char* s_, shared_ptr<object3> o3_): s (s_), o3 (o3_) {} + + #pragma db id auto + int id; + + std::string s; + shared_ptr<object3> o3; + }; + + #pragma db view object(object3) object(object4) + struct view2 + { + shared_ptr<object4> o4; + shared_ptr<object3> o3; + }; +} + +#endif // TEST5_HXX diff --git a/odb-tests/common/view/olv/test6.hxx b/odb-tests/common/view/olv/test6.hxx new file mode 100644 index 0000000..5336fa6 --- /dev/null +++ b/odb-tests/common/view/olv/test6.hxx @@ -0,0 +1,57 @@ +// file : common/view/olv/test6.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST6_HXX +#define TEST6_HXX + +#include <string> +#include <memory> // shared_ptr + +#include <odb/core.hxx> +#include <odb/section.hxx> + +// Test interaction with sections. +// +#pragma db namespace table("t6_") pointer(std::shared_ptr) +namespace test6 +{ + using std::shared_ptr; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const char* s_, shared_ptr<object1> o1_): s (s_), o1 (o1_) {} + + #pragma db id auto + int id; + + std::string s; + + #pragma db load(lazy) + odb::section r; + + #pragma db section(r) + shared_ptr<object1> o1; + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + shared_ptr<object1> o1; + shared_ptr<object2> o2; + }; +} + +#endif // TEST6_HXX diff --git a/odb-tests/common/view/olv/test7.hxx b/odb-tests/common/view/olv/test7.hxx new file mode 100644 index 0000000..dbdc663 --- /dev/null +++ b/odb-tests/common/view/olv/test7.hxx @@ -0,0 +1,57 @@ +// file : common/view/olv/test7.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST7_HXX +#define TEST7_HXX + +#include <string> +#include <memory> // unique_ptr +#include <utility> // std::move + +#include <odb/core.hxx> + +// Test explicit conversion to smart pointer member. +// +#pragma db namespace table("t7_") pointer(*) session +namespace test7 +{ + using std::unique_ptr; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object + struct object2 + { + object2 () {} + object2 (const char* s_, object1* o1_): s (s_), o1 (o1_) {} + + #pragma db id auto + int id; + + std::string s; + object1* o1; // Shallow. + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + // VC12 workaround (no default move constructor generation). + // + view1 () {} + view1 (view1&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {} + + unique_ptr<object2> o2; + unique_ptr<object1> o1; + }; +} + +#endif // TEST7_HXX diff --git a/odb-tests/common/view/olv/test8.hxx b/odb-tests/common/view/olv/test8.hxx new file mode 100644 index 0000000..607d222 --- /dev/null +++ b/odb-tests/common/view/olv/test8.hxx @@ -0,0 +1,54 @@ +// file : common/view/olv/test8.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST8_HXX +#define TEST8_HXX + +#include <string> +#include <memory> // unique_ptr +#include <utility> // std::move + +#include <odb/core.hxx> + +// Test loading objects without id. +// +#pragma db namespace table("t8_") pointer(*) session +namespace test8 +{ + using std::unique_ptr; + + #pragma db object + struct object1 + { + object1 (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object no_id + struct object2 + { + object2 () {} + object2 (const char* s_, object1* o1_): s (s_), o1 (o1_) {} + + std::string s; + object1* o1; // Shallow. + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + // VC12 workaround (no default move constructor generation). + // + view1 () {} + view1 (view1&& x): o2 (std::move (x.o2)), o1 (std::move (x.o1)) {} + + unique_ptr<object2> o2; + unique_ptr<object1> o1; + }; +} + +#endif // TEST8_HXX diff --git a/odb-tests/common/view/olv/test9.hxx b/odb-tests/common/view/olv/test9.hxx new file mode 100644 index 0000000..b109de3 --- /dev/null +++ b/odb-tests/common/view/olv/test9.hxx @@ -0,0 +1,78 @@ +// file : common/view/olv/test9.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST9_HXX +#define TEST9_HXX + +#include <string> +#include <memory> // shared_ptr + +#include <odb/core.hxx> + +// Test loading polymorphic objects. +// +#pragma db namespace table("t9_") session +namespace test9 +{ + using std::shared_ptr; + + #pragma db object polymorphic pointer(shared_ptr) + struct root + { + virtual ~root () {} + root (int n_ = 0): n (n_) {} + + #pragma db id auto + int id; + + int n; + }; + + #pragma db object + struct base: root + { + base (int n_ = 0, const char* s_ = ""): root (n_), s (s_) {} + + std::string s; + }; + + #pragma db object + struct derived: base + { + derived (int n_ = 0, const char* s_ = "", bool b_ = false) + : base (n_, s_), b (b_) {} + + bool b; + }; + + // Load via root. + // + #pragma db view object(root) + struct view1r + { + shared_ptr<root> o; + int n; + }; + + // Load via base. + // + #pragma db view object(base) + struct view1b + { + std::string s; + shared_ptr<base> o; + int n; + }; + + // Load via derived. + // + #pragma db view object(derived) + struct view1d + { + std::string s; + shared_ptr<derived> o; + int n; + }; +} + +#endif // TEST9_HXX diff --git a/odb-tests/common/view/olv/testscript b/odb-tests/common/view/olv/testscript new file mode 100644 index 0000000..5189f19 --- /dev/null +++ b/odb-tests/common/view/olv/testscript @@ -0,0 +1,65 @@ +# file : common/view/olv/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../../mysql-schema.testscript + + for s: $schemas + cat $out_base/"$s"($multi ? '-mysql' : '').sql | $create_schema_cmd + end; + + $* ($multi ? 'mysql' : ) $mysql_options +} + +: sqlite +: +if $sqlite +{ + .include ../../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../../pgsql-schema.testscript + + for s: $schemas + $create_schema_cmd -f $out_base/"$s"($multi ? '-pgsql' : '').sql + end; + + $* ($multi ? 'pgsql' : ) $pgsql_options +} + +: oracle +: +if $oracle +{ + .include ../../../oracle-schema.testscript + + for s: $schemas + $create_schema_cmd "@$out_base/$s"($multi ? '-oracle' : '').sql + end; + + $* ($multi ? 'oracle' : ) $oracle_options +} + +: mssql +: +if $mssql +{ + .include ../../../mssql-schema.testscript + + for s: $schemas + $create_schema_cmd -i $out_base/"$s"($multi ? '-mssql' : '').sql + end; + + $* ($multi ? 'mssql' : ) $mssql_options +} diff --git a/odb-tests/common/virtual/buildfile b/odb-tests/common/virtual/buildfile new file mode 100644 index 0000000..96d062e --- /dev/null +++ b/odb-tests/common/virtual/buildfile @@ -0,0 +1,42 @@ +# file : common/virtual/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 virtual_ \ + --generate-schema \ + --generate-query \ + --generate-session + +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/virtual/driver.cxx b/odb-tests/common/virtual/driver.cxx new file mode 100644 index 0000000..f96f543 --- /dev/null +++ b/odb-tests/common/virtual/driver.cxx @@ -0,0 +1,154 @@ +// file : common/virtual/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test virtual data members. +// + +#include <memory> // std::auto_ptr +#include <iostream> + +#include <odb/session.hxx> +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test basic virtual data member functionality. + // + { + using namespace test1; + + object o; + o.i (123); + o.c1.i = 123; + o.c1.s = "abc"; + o.v1.push_back ("abc"); + o.v1.push_back ("abd"); + o.v1.push_back ("abe"); + o.p1 = new object; + + { + transaction t (db->begin ()); + db->persist (*o.p1); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (o.id1.v)); + t.commit (); + + assert (o == *p); + } + } + + // Test pragma resolution to virtual data member. + // + { + using namespace test2; + + object1 o1 (1); + o1.o2 = new object2 (1); + o1.o2->o1 = &o1; + + { + transaction t (db->begin ()); + db->persist (*o1.o2); + o1.n1 = o1.o2->id; + db->persist (o1); + t.commit (); + } + + { + session s; + transaction t (db->begin ()); + unique_ptr<object1> p (db->load<object1> (o1.id)); + t.commit (); + + assert (p->o2->id == o1.o2->id); + } + + { + typedef odb::query<view1> query; + typedef odb::result<view1> result; + + transaction t (db->begin ()); + result r (db->query<view1> (query::object2::id == o1.o2->id)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->i == o1.n1); + assert (++i == r.end ()); + t.commit (); + } + + { + typedef odb::query<view2> query; + typedef odb::result<view2> result; + + transaction t (db->begin ()); + result r (db->query<view2> (query::o2::id == o1.o2->id)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->i == o1.n1); + assert (++i == r.end ()); + t.commit (); + } + + { + typedef odb::result<view3> result; + + transaction t (db->begin ()); + result r (db->query<view3> ()); + result::iterator i (r.begin ()); + assert (i != r.end () && i->i == o1.n1); + assert (++i == r.end ()); + t.commit (); + } + } + + // Use virtual data members to implement multi-member composite object id. + // + { + using namespace test3; + + person o; + o.first_ = "John"; + o.last_ = "Doe"; + + name id; + { + transaction t (db->begin ()); + id = db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<person> p (db->load<person> (id)); + t.commit (); + + assert (o.first_ == p->first_ && o.last_ == p->last_); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/virtual/test.hxx b/odb-tests/common/virtual/test.hxx new file mode 100644 index 0000000..2654d09 --- /dev/null +++ b/odb-tests/common/virtual/test.hxx @@ -0,0 +1,171 @@ +// file : common/virtual/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <vector> + +#include <odb/core.hxx> + +// Test basic virtual data member functionality. +// +#pragma db namespace table("t1_") +namespace test1 +{ + #pragma db value + struct comp + { + int i; + + #pragma db transient + std::string s; + #pragma db member(s_) virtual(std::string) access(s) + + bool operator== (const comp& v) const + { + return i == v.i && s == v.s; + } + }; + + #pragma db object transient + struct object + { + object (): p1 (0) {} + ~object () {delete p1;} + + struct {unsigned long v;} id1; + + #pragma db id + #pragma db member(id) get(id1.v) virtual(unsigned long) set(id1.v) + #pragma db member(id) auto + + int i () const {return i1;} + void i (int i) {i1 = i;} + int i1; + + comp c1; + #pragma db member(c) virtual(comp) access(c1) + + typedef std::vector<std::string> strings; + strings v1; + #pragma db member(v) virtual(strings) access(v1) + + typedef object* object_ptr; + object_ptr p1; + #pragma db member(p) virtual(object_ptr) access(p1) + + bool operator== (const object& o) const + { + return id1.v == o.id1.v && + i1 == o.i1 && + c1 == o.c1 && + v1 == o.v1 && + (p1 != 0 ? o.p1 != 0 && *p1 == *o.p1 : o.p1 == 0); + } + }; + + #pragma db member(object::i) virtual(int) +} + +#pragma db member(test1::object::id) column("oid") + +// Test pragma resolution to virtual data member. +// +#pragma db namespace table("t2_") +namespace test2 +{ + struct object1; + struct object2; + + typedef object1* object1_ptr; + typedef object2* object2_ptr; + + #pragma db object + struct object2 + { + object2 (unsigned long i = 0): id (i) {} + + #pragma db id + unsigned long id; + + #pragma db inverse(o) + object1_ptr o1; + }; + + #pragma db object + struct object1 + { + object1 (unsigned long i = 0): id (i), o2 (0) {} + ~object1 () {delete o2;} + + #pragma db id + unsigned long id; + + #pragma db transient + object2_ptr o2; + #pragma db member(o) virtual(object2_ptr) access(o2) + + #pragma db transient + unsigned long n1; + #pragma db member(n) virtual(unsigned long) access(n1) + #pragma db index member(n) + }; + + #pragma db view object(object1) object(object2) + struct view1 + { + #pragma db column(object1::n) + unsigned long i; + }; + + #pragma db view object(object1 = o1) object(object2 = o2: o1::n == o2::id) + struct view2 + { + #pragma db column(o1::n) + unsigned long i; + }; + + #pragma db view object(object1: object1::n != 0) + struct view3 + { + #pragma db column(test2::object1::n) + unsigned long i; + }; +} + +// Use virtual data members to implement multi-member composite object id. +// +#pragma db namespace table("t3_") +namespace test3 +{ + #pragma db value + struct name + { + name () {} + name (const std::string& f, const std::string& l) + : first (f), last(l) {} + + std::string first; + std::string last; + + bool operator< (const name& x) const + { + return first < x.first || (first == x.first && last < x.last); + } + }; + + #pragma db object transient + struct person + { + std::string first_; + std::string last_; + + #pragma db member(name) virtual(name) id \ + get(::test3::name (this.first_, this.last_)) \ + set(this.first_ = (?).first; this.last_ = (?).last) + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/virtual/testscript b/odb-tests/common/virtual/testscript new file mode 100644 index 0000000..afdb4fb --- /dev/null +++ b/odb-tests/common/virtual/testscript @@ -0,0 +1,53 @@ +# file : common/virtual/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} diff --git a/odb-tests/common/wrapper/buildfile b/odb-tests/common/wrapper/buildfile new file mode 100644 index 0000000..57f43f2 --- /dev/null +++ b/odb-tests/common/wrapper/buildfile @@ -0,0 +1,40 @@ +# file : common/wrapper/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +import libodb = libodb%lib{odb} + +libs = + +for db: $databases + import libs += libodb-$db%lib{odb-$db} + +import libs += lib{common} + +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 + +<{hxx ixx cxx}{test-odb}>: hxx{test} libue{test-meta} + +for db: $databases +{ + exe{driver}: {hxx ixx cxx}{test-odb-$db}: include = $multi + <{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 wrapper_ \ + --generate-schema + +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/wrapper/driver.cxx b/odb-tests/common/wrapper/driver.cxx new file mode 100644 index 0000000..9c352fc --- /dev/null +++ b/odb-tests/common/wrapper/driver.cxx @@ -0,0 +1,216 @@ +// file : common/wrapper/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test wrapper machinery. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> + +#include <libcommon/common.hxx> + +#include "test.hxx" +#include "test-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +int +main (int argc, char* argv[]) +{ + try + { + unique_ptr<database> db (create_database (argc, argv)); + + // Test 1: simple values. + // + { + using namespace test1; + + unsigned long id1, id2; + { + object1 o1; + object2 o2; + + o1.num.reset (new int (123)); + o1.nstrs.push_back (nullable_string ()); + o1.nstrs.push_back (nullable_string ("123")); + + o2.sstrs.push_back (str_sptr ()); + o2.sstrs.push_back (str_sptr (new string ("123"))); + + transaction t (db->begin ()); + id1 = db->persist (o1); + id2 = db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object1> o1 (db->load<object1> (id1)); + unique_ptr<object2> o2 (db->load<object2> (id2)); + t.commit (); + + assert (*o1->num == 123); + assert (o1->str.get () == 0); + assert (o1->nstr.null ()); + assert (o1->nstrs[0].null ()); + assert (o1->nstrs[1].get () == "123"); + + assert (!o2->sstr); + assert (!o2->sstrs[0]); + assert (*o2->sstrs[1] == "123"); + } + } + + // + // Composite values. + // + unsigned long id; + { + comp_object co; + + co.c1.reset (new comp1 ("123", 123)); + co.vc1.push_back (comp1 ("1", 1)); + co.vc1.push_back (comp1 ("2", 2)); + co.vc1.push_back (comp1 ("3", 3)); + + co.c2.reset (new comp2 ("123", 123)); + co.c2->strs.push_back ("1"); + co.c2->strs.push_back ("2"); + co.c2->strs.push_back ("3"); + + { + transaction t (db->begin ()); + id = db->persist (co); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<comp_object> o (db->load<comp_object> (id)); + t.commit (); + + assert (*o->c1 == *co.c1); + assert (o->vc1 == co.vc1); + assert (*o->c2 == *co.c2); + } + } + + // + // Containers. + // + { + cont_object co; + + co.nums.reset (new vector<int>); + co.nums->push_back (1); + co.nums->push_back (2); + co.nums->push_back (3); + + co.c.num = 123; + co.c.strs.reset (new vector<string>); + co.c.strs->push_back ("1"); + co.c.strs->push_back ("2"); + co.c.strs->push_back ("3"); + + { + transaction t (db->begin ()); + id = db->persist (co); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<cont_object> o (db->load<cont_object> (id)); + t.commit (); + + assert (*o->nums == *co.nums); + assert (o->c == co.c); + } + } + + // Test 5: composite NULL values. + // + { + using namespace test5; + + object o1, o2; + + o1.v.push_back (nullable<comp> ()); + + o2.p.reset (new comp (1, "a")); + o2.n = comp (2, "b"); + o2.v.push_back (comp (3, "c")); + + // Persist. + // + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + // Load. + // + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (p1->p.get () == 0); + assert (!p1->n); + assert (!p1->v[0]); + + assert (p2->p.get () != 0 && *p2->p == *o2.p); + assert (p2->n && *p2->n == *o2.n); + assert (p2->v[0] && *p2->v[0] == *o2.v[0]); + } + + // Update. + // + { + o1.p.reset (new comp (1, "a")); + o1.n = comp (2, "b"); + o1.v[0] = comp (3, "c"); + + o2.p.reset (); + o2.n.reset (); + o2.v[0].reset (); + + transaction t (db->begin ()); + db->update (o1); + db->update (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p1 (db->load<object> (o1.id)); + unique_ptr<object> p2 (db->load<object> (o2.id)); + t.commit (); + + assert (p1->p.get () != 0 && *p1->p == *o1.p); + assert (p1->n && *p1->n == *o1.n); + assert (p1->v[0] && *p1->v[0] == *o1.v[0]); + + assert (p2->p.get () == 0); + assert (!p2->n); + assert (!p2->v[0]); + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/common/wrapper/test.hxx b/odb-tests/common/wrapper/test.hxx new file mode 100644 index 0000000..91f5758 --- /dev/null +++ b/odb-tests/common/wrapper/test.hxx @@ -0,0 +1,214 @@ +// file : common/wrapper/test.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST_HXX +#define TEST_HXX + +#include <string> +#include <memory> // std::unique_ptr +#include <vector> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +using odb::nullable; + +// Test 1: simple values. +// +#pragma db namespace table("t1_") +namespace test1 +{ + typedef nullable<std::string> nullable_string; + + typedef std::unique_ptr<int> num_uptr; + typedef std::unique_ptr<std::string> str_uptr; + typedef std::shared_ptr<std::string> str_sptr; + + #pragma db object table("obj1") + struct object1 + { + #pragma db id auto + unsigned long id_; + + num_uptr num; + + #pragma db null + str_uptr str; + + nullable_string nstr; + std::vector<nullable_string> nstrs; + }; + + #pragma db object + struct object2 + { + #pragma db id auto + unsigned long id_; + + #pragma db null + str_sptr sstr; + + #pragma db value_null + std::vector<str_sptr> sstrs; + }; +} + +// +// Composite values. +// + +#pragma db value +struct comp1 +{ + comp1 (): num (0) {} + comp1 (const std::string& s, int n): str (s), num (n) {} + + std::string str; + int num; +}; + +inline bool +operator== (const comp1& x, const comp1& y) +{ + return x.str == y.str && x.num == y.num; +} + + +#pragma db value +struct comp2 +{ + comp2 () {} + comp2 (const std::string& s, int n): str (s), num (n) {} + + std::string str; + int num; + + std::vector<std::string> strs; +}; + +inline bool +operator== (const comp2& x, const comp2& y) +{ + return x.str == y.str && x.num == y.num && x.strs == y.strs; +} + +struct comp3; + +typedef std::unique_ptr<comp1> comp1_uptr; +typedef std::unique_ptr<comp2> comp2_uptr; +typedef std::unique_ptr<comp3> comp3_uptr; + +#pragma db object +struct comp_object +{ + #pragma db id auto + unsigned long id_; + + comp1_uptr c1; // Wrapped comp value. + std::vector<nullable<comp1> > vc1; // Container of wrapped comp values. + comp2_uptr c2; // Container inside wrapped comp value. +}; + +// This one is just a compilation test to cover more convolute cases. +// +#pragma db value +struct comp3: comp2 +{ + comp1_uptr c1; + std::vector<nullable<comp1> > vc1; +}; + +#pragma db object +struct comp_object2 +{ + #pragma db id auto + unsigned long id_; + + comp3_uptr c3; +}; + +// +// Containers. +// + +typedef std::unique_ptr<std::vector<int>> nums_uptr; +typedef std::unique_ptr<std::vector<std::string>> strs_uptr; + +#pragma db value +struct cont_comp +{ + int num; + strs_uptr strs; +}; + +inline bool +operator== (const cont_comp& x, const cont_comp& y) +{ + return x.num == y.num && *x.strs == *y.strs; +} + +#pragma db object +struct cont_object +{ + #pragma db id auto + unsigned long id_; + + nums_uptr nums; // Wrapped container. + cont_comp c; // Wrapped container in comp value. +}; + +// Test composite NULL values. +// +#pragma db namespace table("t5_") +namespace test5 +{ + #pragma db value + struct base + { + base () {} + base (int n): num (n) {} + + int num = 0; + }; + + inline bool + operator== (const base& x, const base& y) + { + return x.num == y.num; + } + + #pragma db value + struct comp: base + { + comp () {} + comp (int n, const std::string s): base (n), str (s), extra (n + 1) {} + + std::string str; + base extra; + + odb::nullable<int> always_null; + }; + + inline bool + operator== (const comp& x, const comp& y) + { + return static_cast<const base&> (x) == y && + x.str == y.str && x.extra == y.extra; + } + + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + #pragma db null + std::unique_ptr<comp> p; + + odb::nullable<comp> n; + + std::vector< odb::nullable<comp> > v; + }; +} + +#endif // TEST_HXX diff --git a/odb-tests/common/wrapper/testscript b/odb-tests/common/wrapper/testscript new file mode 100644 index 0000000..31c3a97 --- /dev/null +++ b/odb-tests/common/wrapper/testscript @@ -0,0 +1,53 @@ +# file : common/wrapper/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +: mysql +: +if $mysql +{ + .include ../../mysql.testscript + + $create_schema; + $* +} + +: sqlite +: +if $sqlite +{ + .include ../../sqlite.testscript + + $* +} + +: pgsql +: +if $pgsql +{ + .include ../../pgsql.testscript + + $create_schema; + $* +} + +: oracle +: +if $oracle +{ + .include ../../oracle.testscript + + $create_schema; + $* +} + +: mssql +: +if $mssql +{ + .include ../../mssql.testscript + + $create_schema; + $* +} |