diff options
Diffstat (limited to 'odb-tests/evolution')
106 files changed, 10582 insertions, 0 deletions
diff --git a/odb-tests/evolution/.gitignore b/odb-tests/evolution/.gitignore new file mode 100644 index 0000000..9a410c7 --- /dev/null +++ b/odb-tests/evolution/.gitignore @@ -0,0 +1,15 @@ +# ODB-generated files. +# +test1-odb.?xx +test1.sql +model.xml + +test2-odb.?xx +test2.sql + +test3-odb.?xx +test3.sql +test3-002-post.sql +test3-002-pre.sql +test3-003-post.sql +test3-003-pre.sql diff --git a/odb-tests/evolution/add-column/buildfile b/odb-tests/evolution/add-column/buildfile new file mode 100644 index 0000000..efa6b6d --- /dev/null +++ b/odb-tests/evolution/add-column/buildfile @@ -0,0 +1,63 @@ +# file : evolution/add-column/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_add_c_ \ + --schema-version-table evo_add_c_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/add-column/driver.cxx b/odb-tests/evolution/add-column/driver.cxx new file mode 100644 index 0000000..c9a7263 --- /dev/null +++ b/odb-tests/evolution/add-column/driver.cxx @@ -0,0 +1,130 @@ +// file : evolution/add-column/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test adding a new column. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_add_c_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o (1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (!p->str); + assert (p->num == 999); + + // Migration. + // + p->str = "abc"; + p->num = 123; + db->update (*p); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (p->str && *p->str == "abc"); + assert (p->num == 123); + + t.commit (); + } + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/add-column/model.hxx b/odb-tests/evolution/add-column/model.hxx new file mode 100644 index 0000000..6ac9160 --- /dev/null +++ b/odb-tests/evolution/add-column/model.hxx @@ -0,0 +1,38 @@ +// file : evolution/add-column/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 3 + odb::nullable<std::string> str; + + #pragma db default(999) + unsigned long num; +#endif + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/add-column/test1.hxx b/odb-tests/evolution/add-column/test1.hxx new file mode 100644 index 0000000..b0d7fda --- /dev/null +++ b/odb-tests/evolution/add-column/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/add-column/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/add-column/test2.hxx b/odb-tests/evolution/add-column/test2.hxx new file mode 100644 index 0000000..b62530a --- /dev/null +++ b/odb-tests/evolution/add-column/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-column/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/add-column/test3.hxx b/odb-tests/evolution/add-column/test3.hxx new file mode 100644 index 0000000..b24dba1 --- /dev/null +++ b/odb-tests/evolution/add-column/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-column/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/add-column/testscript b/odb-tests/evolution/add-column/testscript new file mode 100644 index 0000000..4741109 --- /dev/null +++ b/odb-tests/evolution/add-column/testscript @@ -0,0 +1,58 @@ +# file : evolution/add-column/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/add-foreign-key/buildfile b/odb-tests/evolution/add-foreign-key/buildfile new file mode 100644 index 0000000..0fe5468 --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/buildfile @@ -0,0 +1,64 @@ +# file : evolution/add-foreign-key/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} +import libodb_db = libodb-$db%lib{odb-$db} +import libcommon = lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-odb} testscript + +# Introduce the metadata library target to make sure the libodb and libcommon +# libraries are resolved for the odb_compile ad hoc rule (see build/root.build +# for details). +# +libue{test-meta}: $libodb $libcommon + +for h: $hdrs + exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +exe{driver}: libue{test-meta} $libodb_db + +# 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 evo_add_fk_ \ + --schema-version-table evo_add_fk_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml \ + --fkeys-deferrable-mode not_deferrable + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/add-foreign-key/driver.cxx b/odb-tests/evolution/add-foreign-key/driver.cxx new file mode 100644 index 0000000..735166f --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/driver.cxx @@ -0,0 +1,178 @@ +// file : evolution/add-foreign-key/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test adding a foreign key. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_add_fk_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o (1); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + +#ifdef DATABASE_SQLITE + // In SQLite we can only add foreign keys inline in the column + // definition. + // + db->connection ()->execute ("PRAGMA foreign_keys=OFF"); +#endif + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Both pointers are now NULL. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (p->o1 == 0 && p->o2 == 0); + + // Migration. The foreign key constraint is not yet there. + // + p->o1 = new object1 (1); + p->o2 = new object2 (1); + db->update (*p); + + t.commit (); + } + + // Migration. Add the missing objects. + // + object1 o1 (1); + object2 o2 (1); + + { + transaction t (db->begin ()); + db->persist (o1); + db->persist (o2); + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->o1->id_ == 1); + assert (p->o2->id_ == 1); + t.commit (); + } + + // Now the foreign key constraint is there. + // + try + { + object o (2); + o.o1 = new object1 (2); + o.o2 = new object2 (2); + + transaction t (db->begin ()); + db->persist (o); + assert (false); + } + catch (const odb::exception& ) {} + + // As well as the NOT NULL. + // +#ifndef DATABASE_SQLITE + try + { + object o (3); + o.o2 = 0; + + transaction t (db->begin ()); + db->persist (o); + assert (false); + } + catch (const odb::exception& ) {} +#endif + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/add-foreign-key/model.hxx b/odb-tests/evolution/add-foreign-key/model.hxx new file mode 100644 index 0000000..17cd85f --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/model.hxx @@ -0,0 +1,66 @@ +// file : evolution/add-foreign-key/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ +#if MODEL_VERSION == 3 + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + }; + + #pragma db object + struct object2 + { + object2 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + }; +#endif + + #pragma db object + struct object + { + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 2 + object (unsigned long id = 0): id_ (id) {} +#else + object1* o1; + + // There is no support for changing a column to NOT NULL in SQLite. + // +#ifndef ODB_DATABASE_SQLITE + #pragma db not_null +#endif + object2* o2; + + object (unsigned long id = 0): id_ (id), o1 (0), o2 (0) {} + ~object () {delete o1; delete o2;} +#endif + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/add-foreign-key/test1.hxx b/odb-tests/evolution/add-foreign-key/test1.hxx new file mode 100644 index 0000000..05c78c3 --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/add-foreign-key/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/add-foreign-key/test2.hxx b/odb-tests/evolution/add-foreign-key/test2.hxx new file mode 100644 index 0000000..c57d9a1 --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-foreign-key/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/add-foreign-key/test3.hxx b/odb-tests/evolution/add-foreign-key/test3.hxx new file mode 100644 index 0000000..c844469 --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-foreign-key/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/add-foreign-key/testscript b/odb-tests/evolution/add-foreign-key/testscript new file mode 100644 index 0000000..9ea393f --- /dev/null +++ b/odb-tests/evolution/add-foreign-key/testscript @@ -0,0 +1,58 @@ +# file : evolution/add-foreign-key/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/add-index/buildfile b/odb-tests/evolution/add-index/buildfile new file mode 100644 index 0000000..9835d1e --- /dev/null +++ b/odb-tests/evolution/add-index/buildfile @@ -0,0 +1,63 @@ +# file : evolution/add-index/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_add_i_ \ + --schema-version-table evo_add_i_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/add-index/driver.cxx b/odb-tests/evolution/add-index/driver.cxx new file mode 100644 index 0000000..6b1d15e --- /dev/null +++ b/odb-tests/evolution/add-index/driver.cxx @@ -0,0 +1,170 @@ +// file : evolution/add-index/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test adding a new index. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_add_i_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o0 (0); + o0.num = 123; + + object o1 (1); + o1.num = 234; + + object o2 (2); + o2.num = 234; + + // Duplicates are ok. + // + { + transaction t (db->begin ()); + db->persist (o0); + db->persist (o1); + db->persist (o2); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + object o3 (3); + o3.num = 234; + + // Duplicates are still ok but we need to remove them before the + // post migration step. + // + { + transaction t (db->begin ()); + db->persist (o3); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> ( + "ORDER BY" + query::num + "," + query::id)); + + unsigned long prev (0); + for (result::iterator i (r.begin ()); i != r.end (); ++i) + { + if (i->num == prev) + db->erase (*i); + + prev = i->num; + } + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p0 (db->load<object> (0)); + unique_ptr<object> p1 (db->load<object> (1)); + + assert (p0->num == 123); + assert (p1->num == 234); + + t.commit (); + } + + try + { + object o2 (2); + o2.num = 234; + + transaction t (db->begin ()); + db->persist (o2); + assert (false); + } + catch (const odb::exception& ) {} + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/add-index/model.hxx b/odb-tests/evolution/add-index/model.hxx new file mode 100644 index 0000000..fec75cc --- /dev/null +++ b/odb-tests/evolution/add-index/model.hxx @@ -0,0 +1,33 @@ +// file : evolution/add-index/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 3 + #pragma db unique +#endif + unsigned long num; + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/add-index/test1.hxx b/odb-tests/evolution/add-index/test1.hxx new file mode 100644 index 0000000..1be2b5b --- /dev/null +++ b/odb-tests/evolution/add-index/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/add-index/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/add-index/test2.hxx b/odb-tests/evolution/add-index/test2.hxx new file mode 100644 index 0000000..a0faca9 --- /dev/null +++ b/odb-tests/evolution/add-index/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-index/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/add-index/test3.hxx b/odb-tests/evolution/add-index/test3.hxx new file mode 100644 index 0000000..aab9c86 --- /dev/null +++ b/odb-tests/evolution/add-index/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-index/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/add-index/testscript b/odb-tests/evolution/add-index/testscript new file mode 100644 index 0000000..e71352a --- /dev/null +++ b/odb-tests/evolution/add-index/testscript @@ -0,0 +1,58 @@ +# file : evolution/add-index/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/add-table/buildfile b/odb-tests/evolution/add-table/buildfile new file mode 100644 index 0000000..d17cd70 --- /dev/null +++ b/odb-tests/evolution/add-table/buildfile @@ -0,0 +1,63 @@ +# file : evolution/add-table/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_add_t_ \ + --schema-version-table evo_add_t_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/add-table/driver.cxx b/odb-tests/evolution/add-table/driver.cxx new file mode 100644 index 0000000..1793553 --- /dev/null +++ b/odb-tests/evolution/add-table/driver.cxx @@ -0,0 +1,145 @@ +// file : evolution/add-table/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test adding a new table (object, container). +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_add_t_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + // SQLite has broken foreign keys when it comes to DDL. + // +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=OFF"); +#endif + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=ON"); +#endif + } + + object o (1); + o.str = "abc"; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + object1 o1; + o1.nums.push_back (1); + o1.nums.push_back (2); + o1.nums.push_back (3); + + { + transaction t (db->begin ()); + o1.o = db->load<object> (1); + db->persist (o1); + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + typedef odb::query<object1> query; + typedef odb::result<object1> result; + + { + transaction t (db->begin ()); + + result r (db->query<object1> (query::o->str == "abc")); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e && + i->o->id_ == 1 && + i->nums[0] == 1 && i->nums[1] == 2 && i->nums[2] == 3); + assert (++i == e); + + t.commit (); + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/add-table/model.hxx b/odb-tests/evolution/add-table/model.hxx new file mode 100644 index 0000000..208a156 --- /dev/null +++ b/odb-tests/evolution/add-table/model.hxx @@ -0,0 +1,48 @@ +// file : evolution/add-table/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <vector> +#include <string> + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db object + struct object1 + { + object1 (): o (0) {} + ~object1 () {delete o;} + + #pragma db id auto + unsigned long id_; + + object* o; + std::vector<int> nums; + }; +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/add-table/test1.hxx b/odb-tests/evolution/add-table/test1.hxx new file mode 100644 index 0000000..adc51a1 --- /dev/null +++ b/odb-tests/evolution/add-table/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/add-table/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/add-table/test2.hxx b/odb-tests/evolution/add-table/test2.hxx new file mode 100644 index 0000000..ca03cef --- /dev/null +++ b/odb-tests/evolution/add-table/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-table/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/add-table/test3.hxx b/odb-tests/evolution/add-table/test3.hxx new file mode 100644 index 0000000..39406a2 --- /dev/null +++ b/odb-tests/evolution/add-table/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/add-table/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/add-table/testscript b/odb-tests/evolution/add-table/testscript new file mode 100644 index 0000000..fad1225 --- /dev/null +++ b/odb-tests/evolution/add-table/testscript @@ -0,0 +1,58 @@ +# file : evolution/add-table/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/alter-column/buildfile b/odb-tests/evolution/alter-column/buildfile new file mode 100644 index 0000000..08b0ed7 --- /dev/null +++ b/odb-tests/evolution/alter-column/buildfile @@ -0,0 +1,63 @@ +# file : evolution/alter-column/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} +import libodb_db = libodb-$db%lib{odb-$db} +import libcommon = lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-odb} testscript + +# Introduce the metadata library target to make sure the libodb and libcommon +# libraries are resolved for the odb_compile ad hoc rule (see build/root.build +# for details). +# +libue{test-meta}: $libodb $libcommon + +for h: $hdrs + exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +exe{driver}: libue{test-meta} $libodb_db + +# 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 evo_alter_c_ \ + --schema-version-table evo_alter_c_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/alter-column/driver.cxx b/odb-tests/evolution/alter-column/driver.cxx new file mode 100644 index 0000000..cf7cbc3 --- /dev/null +++ b/odb-tests/evolution/alter-column/driver.cxx @@ -0,0 +1,164 @@ +// file : evolution/alter-column/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test altering a column. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_alter_c_sv")); + + // SQLite doesn't support altering of columns. + // +#ifndef DATABASE_SQLITE + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o (1); + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // NULL is already in effect; NOT NULL is not yet. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (!p->str); + assert (p->num && *p->num == 123); + assert (!p->num1); + + // Migration. + // + p->str = "abc"; + p->num.reset (); + p->num1 = 123; + db->update (*p); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (p->str && *p->str == "abc"); + assert (!p->num); + + t.commit (); + } + + // NOT NULL is now in effect. + // + try + { + object o2 (2); + o2.num1 = 234; // str is NULL + + transaction t (db->begin ()); + db->persist (o2); + assert (false); + } + catch (const odb::exception& ) {} + + try + { + object o3 (3); + o3.str = "bcd"; // num1 is NULL + + transaction t (db->begin ()); + db->persist (o3); + assert (false); + } + catch (const odb::exception& ) {} + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } +#endif // DATABASE_SQLITE + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/alter-column/model.hxx b/odb-tests/evolution/alter-column/model.hxx new file mode 100644 index 0000000..8378887 --- /dev/null +++ b/odb-tests/evolution/alter-column/model.hxx @@ -0,0 +1,56 @@ +// file : evolution/alter-column/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + // SQLite doesn't support altering of columns. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + odb::nullable<std::string> str; + + unsigned long num; +#else + // Use nullable to be able to access during migration. + // + #pragma db not_null + odb::nullable<std::string> str; + + odb::nullable<unsigned long> num; + + // Test adding NOT NULL column. It should be added NULL in pre + // and then converted to NOT NULL in post. + // + #pragma db not_null + odb::nullable<unsigned long> num1; +#endif +#endif + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/alter-column/test1.hxx b/odb-tests/evolution/alter-column/test1.hxx new file mode 100644 index 0000000..9353558 --- /dev/null +++ b/odb-tests/evolution/alter-column/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/alter-column/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/alter-column/test2.hxx b/odb-tests/evolution/alter-column/test2.hxx new file mode 100644 index 0000000..e2dbed3 --- /dev/null +++ b/odb-tests/evolution/alter-column/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/alter-column/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/alter-column/test3.hxx b/odb-tests/evolution/alter-column/test3.hxx new file mode 100644 index 0000000..ac08e2f --- /dev/null +++ b/odb-tests/evolution/alter-column/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/alter-column/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/alter-column/testscript b/odb-tests/evolution/alter-column/testscript new file mode 100644 index 0000000..ff8f9a6 --- /dev/null +++ b/odb-tests/evolution/alter-column/testscript @@ -0,0 +1,59 @@ +# file : evolution/alter-column/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + # Note that SQLite doesn't support altering of columns and so the driver is + # trivial. We, however, still run it once for good measure. + # + $* 1 +} diff --git a/odb-tests/evolution/combined/buildfile b/odb-tests/evolution/combined/buildfile new file mode 100644 index 0000000..0ccce68 --- /dev/null +++ b/odb-tests/evolution/combined/buildfile @@ -0,0 +1,64 @@ +# file : evolution/combined/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} +import libodb_db = libodb-$db%lib{odb-$db} +import libcommon = lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-odb} testscript + +# Introduce the metadata library target to make sure the libodb and libcommon +# libraries are resolved for the odb_compile ad hoc rule (see build/root.build +# for details). +# +libue{test-meta}: $libodb $libcommon + +for h: $hdrs + exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +exe{driver}: libue{test-meta} $libodb_db + +# 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 evo_comb_ \ + --schema-version-table evo_comb_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml \ + --sqlite-override-null + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/combined/driver.cxx b/odb-tests/evolution/combined/driver.cxx new file mode 100644 index 0000000..2b77437 --- /dev/null +++ b/odb-tests/evolution/combined/driver.cxx @@ -0,0 +1,162 @@ +// file : evolution/combined/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Combined schema evolution test. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_comb_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o ("1"); + o.dui = 1; + o.anui = 1; + o.dnui = 1; + o.dc = 1; + o.dt.push_back (1); + o.aui = 1; + +#ifndef DATABASE_SQLITE + o.dfk = new object1 (1); + o.acn = 1; + o.acnn.reset (); + o.afk = 1; +#endif + + { + transaction t (db->begin ()); +#ifndef DATABASE_SQLITE + db->persist (o.dfk); +#endif + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> ("1")); + + assert (p->ac1 == 999); + assert (!p->ac2); + +#ifndef DATABASE_SQLITE + assert (!p->ac3); +#endif + // Migrate. + // + p->at.push_back ("abc"); + +#ifndef DATABASE_SQLITE + p->dfk = 999; + p->ac3 = 1; + p->acn.reset (); + p->acnn = 1; +#endif + db->update (*p); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> ("1")); + + // Check post-migration. + // + assert (p->at[0] == "abc"); + +#ifndef DATABASE_SQLITE + assert (p->dfk == 999); + assert (*p->ac3 == 1); + assert (!p->acn); + assert (*p->acnn == 1); +#endif + t.commit (); + } + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/combined/model.hxx b/odb-tests/evolution/combined/model.hxx new file mode 100644 index 0000000..5f1180b --- /dev/null +++ b/odb-tests/evolution/combined/model.hxx @@ -0,0 +1,174 @@ +// file : evolution/combined/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> +#include <vector> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + }; + + #pragma db object + struct object + { + #pragma db id + std::string id_; + + // + // Pre pass 1. + // + + // Drop unique index. + // +#if MODEL_VERSION == 2 + #pragma db unique +#endif + unsigned long dui; + + // Alter table drop foreign key. Not supported by SQLite. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + object1* dfk; +#else + #pragma db null + unsigned long dfk; +#endif +#endif + + // Add table. + // +#if MODEL_VERSION == 3 + std::vector<std::string> at; +#endif + + // Add column. + // +#if MODEL_VERSION == 3 + #pragma db default(999) + unsigned long ac1; + + odb::nullable<unsigned long> ac2; + + // Initially added as NULL, converted to NOT NULL. Not supported by SQLite. + // +#ifndef DATABASE_SQLITE + #pragma db not_null + odb::nullable<unsigned long> ac3; +#endif +#endif + + // Alter column NULL. Not supported by SQLite. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + unsigned long acn; +#else + odb::nullable<unsigned long> acn; +#endif +#endif + + // + // Pre pass 2. + // + + // Add non-unique indexes. + // +#if MODEL_VERSION == 3 + #pragma db index +#endif + unsigned long anui; + + // + // Post pass 1. + // + + // Drop non-unique indexes. + // +#if MODEL_VERSION == 2 + #pragma db index +#endif + unsigned long dnui; + + // + // Post pass 2. + // + + // Drop table. + // +#if MODEL_VERSION == 2 + std::vector<unsigned long> dt; +#endif + + // Drop column. Logical drop (set NULL) in SQLite. + // +#if MODEL_VERSION == 2 + unsigned long dc; +#endif + + // Alter column NOT NULL. Not supported by SQLite. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 3 + #pragma db not_null +#endif + odb::nullable<unsigned long> acnn; +#endif + + // Alter table add foreign key. Not supported by SQLite. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + #pragma db null + unsigned long afk; +#else + object1* afk; +#endif +#endif + + // Add unique index. + // +#if MODEL_VERSION == 3 + #pragma db unique +#endif + unsigned long aui; + + public: +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + + object (std::string id = ""): id_ (id), dfk (0) {} + ~object () {delete dfk;} +#else + object (std::string id = ""): id_ (id), afk (0) {} + ~object () {delete afk;} +#endif +#else + object (std::string id = ""): id_ (id) {} +#endif + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/combined/test1.hxx b/odb-tests/evolution/combined/test1.hxx new file mode 100644 index 0000000..1a18aff --- /dev/null +++ b/odb-tests/evolution/combined/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/combined/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/combined/test2.hxx b/odb-tests/evolution/combined/test2.hxx new file mode 100644 index 0000000..8eb7ac8 --- /dev/null +++ b/odb-tests/evolution/combined/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/combined/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/combined/test3.hxx b/odb-tests/evolution/combined/test3.hxx new file mode 100644 index 0000000..05a052f --- /dev/null +++ b/odb-tests/evolution/combined/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/combined/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/combined/testscript b/odb-tests/evolution/combined/testscript new file mode 100644 index 0000000..118e43b --- /dev/null +++ b/odb-tests/evolution/combined/testscript @@ -0,0 +1,58 @@ +# file : evolution/combined/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/data/buildfile b/odb-tests/evolution/data/buildfile new file mode 100644 index 0000000..d013cab --- /dev/null +++ b/odb-tests/evolution/data/buildfile @@ -0,0 +1,63 @@ +# file : evolution/data/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_data_ \ + --schema-version-table evo_data_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/data/driver.cxx b/odb-tests/evolution/data/driver.cxx new file mode 100644 index 0000000..685f556 --- /dev/null +++ b/odb-tests/evolution/data/driver.cxx @@ -0,0 +1,179 @@ +// file : evolution/data/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test data migration support. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-odb.hxx" + +#undef NDEBUG +#include <cassert> + +using namespace std; +using namespace odb::core; + +void +migrate1 (database& db) +{ + using namespace v2; + using namespace v3; + + unique_ptr<object1> o1 (db.load<object1> (1)); + object2 o2 (1); + o2.num = o1->num; + db.persist (o2); +} + +void +migrate2 (database& db) +{ + using namespace v2; + using namespace v3; + + unique_ptr<object1> o1 (db.load<object1> (2)); + object2 o2 (2); + o2.num = o1->num; + db.persist (o2); +} + +static const data_migration_entry<3, 1> migrate2_entry (&migrate2); + +int +main (int argc, char* argv[]) +{ + schema_catalog::data_migration_function<3, 1> (&migrate1); + + schema_catalog::data_migration_function<3, 1> ( + [] (database& db) + { + using namespace v2; + using namespace v3; + + unique_ptr<object1> o1 (db.load<object1> (11)); + object2 o2 (11); + o2.num = o1->num; + db.persist (o2); + }); + + try + { + unique_ptr<database> db (create_database (argc, argv, false)); + + db->schema_version_table (quote_name ("evo_data_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + { + transaction t (db->begin ()); + + { + object1 o1 (1); + o1.num = 123; + db->persist (o1); + } + + { + object1 o1 (2); + o1.num = 123; + db->persist (o1); + } + + { + object1 o1 (11); + o1.num = 123; + db->persist (o1); + } + + t.commit (); + } + break; + } + case 2: + { + { + transaction t (db->begin ()); + + if (embedded) + schema_catalog::migrate (*db); + else + schema_catalog::migrate_data (*db); + + t.commit (); + } + + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + + { + unique_ptr<object2> o2 (db->load<object2> (1)); + assert (o2->num == 123); + } + + { + unique_ptr<object2> o2 (db->load<object2> (2)); + assert (o2->num == 123); + } + + { + unique_ptr<object2> o2 (db->load<object2> (11)); + assert (o2->num == 123); + } + + t.commit (); + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/data/model.hxx b/odb-tests/evolution/data/model.hxx new file mode 100644 index 0000000..680bc55 --- /dev/null +++ b/odb-tests/evolution/data/model.hxx @@ -0,0 +1,45 @@ +// file : evolution/data/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ +#if MODEL_VERSION == 2 + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif + +#if MODEL_VERSION == 3 + #pragma db object + struct object2 + { + object2 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/data/test1.hxx b/odb-tests/evolution/data/test1.hxx new file mode 100644 index 0000000..976ed04 --- /dev/null +++ b/odb-tests/evolution/data/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/data/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/data/test2.hxx b/odb-tests/evolution/data/test2.hxx new file mode 100644 index 0000000..bd72940 --- /dev/null +++ b/odb-tests/evolution/data/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/data/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/data/test3.hxx b/odb-tests/evolution/data/test3.hxx new file mode 100644 index 0000000..0f47099 --- /dev/null +++ b/odb-tests/evolution/data/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/data/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/data/testscript b/odb-tests/evolution/data/testscript new file mode 100644 index 0000000..dddc909 --- /dev/null +++ b/odb-tests/evolution/data/testscript @@ -0,0 +1,58 @@ +# file : evolution/data/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/drop-column/buildfile b/odb-tests/evolution/drop-column/buildfile new file mode 100644 index 0000000..c508f76 --- /dev/null +++ b/odb-tests/evolution/drop-column/buildfile @@ -0,0 +1,64 @@ +# file : evolution/drop-column/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_drop_c_ \ + --schema-version-table evo_drop_c_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml \ + --sqlite-override-null + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/drop-column/driver.cxx b/odb-tests/evolution/drop-column/driver.cxx new file mode 100644 index 0000000..6f3008b --- /dev/null +++ b/odb-tests/evolution/drop-column/driver.cxx @@ -0,0 +1,131 @@ +// file : evolution/drop-column/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test dropping a column. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_drop_c_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o (1); + o.str = "abc"; + o.num = 123; + o.ptr = new object1 (1, 2); + + { + transaction t (db->begin ()); + db->persist (*o.ptr); + db->persist (o); + t.commit (); + } + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Things are still there. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (p->str == "abc"); + assert (p->num == 123); + assert (p->ptr->id.x == 1 && p->ptr->id.y == 2); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + // Now they are gone. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "" && p->ptr == 0); + db->erase<object1> (value (1, 2)); // SQLite logical delete test. + t.commit (); + } + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/drop-column/model.hxx b/odb-tests/evolution/drop-column/model.hxx new file mode 100644 index 0000000..09b63b9 --- /dev/null +++ b/odb-tests/evolution/drop-column/model.hxx @@ -0,0 +1,59 @@ +// file : evolution/drop-column/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> +#include <odb/nullable.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db value + struct value + { + value (int x_ = 0, int y_ = 0): x (x_), y (y_) {} + int x; + int y; + }; + + #pragma db object + struct object1 + { + object1 (int x = 0, int y = 0): id (x, y) {} + + #pragma db id + value id; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id), ptr (0) {} + ~object () {delete ptr;} + + #pragma db id + unsigned long id_; + + std::string str; + unsigned long num; + object1* ptr; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) + #pragma db member(object::num) deleted(3) + #pragma db member(object::ptr) deleted(3) +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/drop-column/test1.hxx b/odb-tests/evolution/drop-column/test1.hxx new file mode 100644 index 0000000..1efb1fa --- /dev/null +++ b/odb-tests/evolution/drop-column/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/drop-column/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/drop-column/test2.hxx b/odb-tests/evolution/drop-column/test2.hxx new file mode 100644 index 0000000..2842cd3 --- /dev/null +++ b/odb-tests/evolution/drop-column/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-column/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/drop-column/test3.hxx b/odb-tests/evolution/drop-column/test3.hxx new file mode 100644 index 0000000..d1ce616 --- /dev/null +++ b/odb-tests/evolution/drop-column/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-column/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/drop-column/testscript b/odb-tests/evolution/drop-column/testscript new file mode 100644 index 0000000..bfda778 --- /dev/null +++ b/odb-tests/evolution/drop-column/testscript @@ -0,0 +1,58 @@ +# file : evolution/drop-column/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/drop-foreign-key/buildfile b/odb-tests/evolution/drop-foreign-key/buildfile new file mode 100644 index 0000000..6d1441e --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/buildfile @@ -0,0 +1,64 @@ +# file : evolution/drop-foreign-key/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} +import libodb_db = libodb-$db%lib{odb-$db} +import libcommon = lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-odb} testscript + +# Introduce the metadata library target to make sure the libodb and libcommon +# libraries are resolved for the odb_compile ad hoc rule (see build/root.build +# for details). +# +libue{test-meta}: $libodb $libcommon + +for h: $hdrs + exe{driver}: {hxx ixx cxx}{$h-odb}: hxx{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +exe{driver}: libue{test-meta} $libodb_db + +# 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 evo_drop_fk_ \ + --schema-version-table evo_drop_fk_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml \ + --fkeys-deferrable-mode not_deferrable + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/drop-foreign-key/driver.cxx b/odb-tests/evolution/drop-foreign-key/driver.cxx new file mode 100644 index 0000000..5556cce --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/driver.cxx @@ -0,0 +1,149 @@ +// file : evolution/drop-foreign-key/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test dropping a foreign key. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_drop_fk_sv")); + + // SQLite doesn't support dropping of foreign keys. + // +#ifndef DATABASE_SQLITE + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o (1); + o.o1 = new object (2); + o.o2 = new object (3); + + { + transaction t (db->begin ()); + db->persist (o.o1); + db->persist (o.o2); + db->persist (o); + t.commit (); + } + + // The foreign key constraint is there. + // + try + { + object o (11); + o.o1 = new object (12); + o.o2 = new object (13); + + transaction t (db->begin ()); + db->persist (o); + assert (false); + } + catch (const odb::exception& ) {} + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // The data is still there but the constraints are gone. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + assert (p->o1 == 2); + assert (p->o2 == 3); + + db->erase<object> (p->o1); + db->erase<object> (p->o2); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->o1 == 2); + assert (p->o2 == 3); + t.commit (); + } + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } +#endif // DATABASE_SQLITE + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/drop-foreign-key/model.hxx b/odb-tests/evolution/drop-foreign-key/model.hxx new file mode 100644 index 0000000..a74d061 --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/model.hxx @@ -0,0 +1,50 @@ +// file : evolution/drop-foreign-key/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + #pragma db id + unsigned long id_; + + // SQLite doesn't support dropping of foreign keys. + // +#ifndef DATABASE_SQLITE +#if MODEL_VERSION == 2 + object* o1; + object* o2; + + object (unsigned long id = 0): id_ (id), o1 (0), o2 (0) {} + ~object () {delete o1; delete o2;} +#else + #pragma db null + unsigned long o1; + + #pragma db null + unsigned long o2; + + object (unsigned long id = 0): id_ (id) {} +#endif +#endif + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/drop-foreign-key/test1.hxx b/odb-tests/evolution/drop-foreign-key/test1.hxx new file mode 100644 index 0000000..5476796 --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/drop-foreign-key/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/drop-foreign-key/test2.hxx b/odb-tests/evolution/drop-foreign-key/test2.hxx new file mode 100644 index 0000000..f091a25 --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-foreign-key/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/drop-foreign-key/test3.hxx b/odb-tests/evolution/drop-foreign-key/test3.hxx new file mode 100644 index 0000000..beb8c42 --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-foreign-key/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/drop-foreign-key/testscript b/odb-tests/evolution/drop-foreign-key/testscript new file mode 100644 index 0000000..f48e801 --- /dev/null +++ b/odb-tests/evolution/drop-foreign-key/testscript @@ -0,0 +1,59 @@ +# file : evolution/drop-foreign-key/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + # Note that SQLite doesn't support dropping of foreign keys and so the + # driver is trivial. We, however, still run it once for good measure. + # + $* 1 +} diff --git a/odb-tests/evolution/drop-index/buildfile b/odb-tests/evolution/drop-index/buildfile new file mode 100644 index 0000000..16cf57e --- /dev/null +++ b/odb-tests/evolution/drop-index/buildfile @@ -0,0 +1,63 @@ +# file : evolution/drop-index/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_drop_i_ \ + --schema-version-table evo_drop_i_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/drop-index/driver.cxx b/odb-tests/evolution/drop-index/driver.cxx new file mode 100644 index 0000000..2b06ca7 --- /dev/null +++ b/odb-tests/evolution/drop-index/driver.cxx @@ -0,0 +1,159 @@ +// file : evolution/drop-index/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test dropping an index. +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_drop_i_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + object o0 (0); + o0.num = 123; + + object o1 (1); + o1.num = 234; + + object o2 (2); + o2.num = 234; + + { + transaction t (db->begin ()); + db->persist (o0); + db->persist (o1); + t.commit (); + } + + // Duplicates are not ok. + // + try + { + transaction t (db->begin ()); + db->persist (o2); + assert (false); + } + catch (const odb::exception& ) {} + + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Duplicates are now ok. + // + object o2 (2); + o2.num = 234; + + { + transaction t (db->begin ()); + db->persist (o2); + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + { + transaction t (db->begin ()); + unique_ptr<object> p0 (db->load<object> (0)); + unique_ptr<object> p1 (db->load<object> (1)); + unique_ptr<object> p2 (db->load<object> (2)); + + assert (p0->num == 123); + assert (p1->num == 234); + assert (p2->num == 234); + + t.commit (); + } + + // Duplicates are still ok. + // + object o3 (3); + o3.num = 234; + + { + transaction t (db->begin ()); + db->persist (o3); + t.commit (); + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/drop-index/model.hxx b/odb-tests/evolution/drop-index/model.hxx new file mode 100644 index 0000000..5e163ca --- /dev/null +++ b/odb-tests/evolution/drop-index/model.hxx @@ -0,0 +1,33 @@ +// file : evolution/drop-index/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 2 + #pragma db unique +#endif + unsigned long num; + }; +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/drop-index/test1.hxx b/odb-tests/evolution/drop-index/test1.hxx new file mode 100644 index 0000000..5a279bb --- /dev/null +++ b/odb-tests/evolution/drop-index/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/drop-index/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/drop-index/test2.hxx b/odb-tests/evolution/drop-index/test2.hxx new file mode 100644 index 0000000..78b89f2 --- /dev/null +++ b/odb-tests/evolution/drop-index/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-index/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/drop-index/test3.hxx b/odb-tests/evolution/drop-index/test3.hxx new file mode 100644 index 0000000..17b670d --- /dev/null +++ b/odb-tests/evolution/drop-index/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-index/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/drop-index/testscript b/odb-tests/evolution/drop-index/testscript new file mode 100644 index 0000000..0fb21ba --- /dev/null +++ b/odb-tests/evolution/drop-index/testscript @@ -0,0 +1,58 @@ +# file : evolution/drop-index/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/drop-table/buildfile b/odb-tests/evolution/drop-table/buildfile new file mode 100644 index 0000000..373ae9f --- /dev/null +++ b/odb-tests/evolution/drop-table/buildfile @@ -0,0 +1,63 @@ +# file : evolution/drop-table/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_drop_t_ \ + --schema-version-table evo_drop_t_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/drop-table/driver.cxx b/odb-tests/evolution/drop-table/driver.cxx new file mode 100644 index 0000000..74407da --- /dev/null +++ b/odb-tests/evolution/drop-table/driver.cxx @@ -0,0 +1,173 @@ +// file : evolution/drop-table/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test dropping a table (object, container). +// + +#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 "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_drop_t_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + { + using namespace v2; + + object1 o1; + o1.o = new object (1); + o1.o->str = "abc"; + o1.nums.push_back (1); + o1.nums.push_back (2); + o1.nums.push_back (3); + + { + transaction t (db->begin ()); + db->persist (o1.o); + db->persist (o1); + t.commit (); + } + } + + // Polymorphism test. + // + { + // We have to use v3 here because the discriminator includes + // the namespace. + // + using namespace v3; + + base b (123, "abc"); + derived d1 (234, "bcd"); + derived d2 (345, "cde"); + + { + transaction t (db->begin ()); + db->persist (b); + db->persist (d1); + db->persist (d2); + t.commit (); + } + } + + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Both object and object1 are still there so we can migrate the data. + // + typedef odb::query<object1> query; + typedef odb::result<object1> result; + + { + transaction t (db->begin ()); + + result r (db->query<object1> (query::o->str == "abc")); + result::iterator i (r.begin ()), e (r.end ()); + + assert (i != e && + i->o->id_ == 1 && + i->nums[0] == 1 && i->nums[1] == 2 && i->nums[2] == 3); + assert (++i == e); + + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + + break; + } + case 3: + { + using namespace v3; + + // Only object is still there. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc"); + t.commit (); + } + + // Polymorphism test. + // + { + transaction t (db->begin ()); + assert (size (db->query<root> ()) == 1); + assert (size (db->query<base> ()) == 1); + t.commit (); + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/drop-table/model.hxx b/odb-tests/evolution/drop-table/model.hxx new file mode 100644 index 0000000..2c02d09 --- /dev/null +++ b/odb-tests/evolution/drop-table/model.hxx @@ -0,0 +1,94 @@ +// file : evolution/drop-table/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <vector> +#include <string> + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string str; + }; + + #pragma db object + struct object1 + { + object1 (): o (0) {} + ~object1 () {delete o;} + + #pragma db id auto + unsigned long id_; + + object* o; + std::vector<int> nums; + }; + +#if MODEL_VERSION == 3 + #pragma db object(object1) deleted(3) +#endif + + // Make sure we also clean up base tables when dropping a + // table corresponding to the polymorphic derived object. + // + #pragma db value + struct value + { + value (unsigned long n = 0, const std::string& s = ""): num (n), str (s) {} + + unsigned long num; + std::string str; + }; + + #pragma db object polymorphic + struct root + { + root (unsigned long n = 0, const std::string& s = ""): id (n, s) {} + virtual ~root () {} + + #pragma db id + value id; + }; + + #pragma db object + struct base: root + { + base (unsigned long n = 0, const std::string& s = "") + : root (n, s), num (n) {} + + unsigned long num; + }; + + #pragma db object + struct derived: base + { + derived (unsigned long n = 0, const std::string& s = "") + : base (n, s), str (s) {} + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db object(derived) deleted(3) +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/drop-table/test1.hxx b/odb-tests/evolution/drop-table/test1.hxx new file mode 100644 index 0000000..a9ec64c --- /dev/null +++ b/odb-tests/evolution/drop-table/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/drop-table/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/drop-table/test2.hxx b/odb-tests/evolution/drop-table/test2.hxx new file mode 100644 index 0000000..c9ec149 --- /dev/null +++ b/odb-tests/evolution/drop-table/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-table/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/drop-table/test3.hxx b/odb-tests/evolution/drop-table/test3.hxx new file mode 100644 index 0000000..8d3f17b --- /dev/null +++ b/odb-tests/evolution/drop-table/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/drop-table/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/drop-table/testscript b/odb-tests/evolution/drop-table/testscript new file mode 100644 index 0000000..6263168 --- /dev/null +++ b/odb-tests/evolution/drop-table/testscript @@ -0,0 +1,58 @@ +# file : evolution/drop-table/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/embedded/buildfile b/odb-tests/evolution/embedded/buildfile new file mode 100644 index 0000000..095e476 --- /dev/null +++ b/odb-tests/evolution/embedded/buildfile @@ -0,0 +1,59 @@ +# file : evolution/embedded/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_embedded_ \ + --schema-version-table evo_embedded_sv \ + --generate-schema \ + --schema-format embedded \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration +<{hxx ixx cxx}{test3-odb}>: odb_options += --omit-create + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/embedded/driver.cxx b/odb-tests/evolution/embedded/driver.cxx new file mode 100644 index 0000000..2bdb2d1 --- /dev/null +++ b/odb-tests/evolution/embedded/driver.cxx @@ -0,0 +1,185 @@ +// file : evolution/embedded/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test embedded schema migration. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#ifdef DATABASE_PGSQL +# include <odb/pgsql/connection.hxx> +#endif + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_embedded_sv")); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + t.commit (); + } + + // PostgreSQL cannot continue a transaction after a query failed. We + // have a workaround but only for 9.4+. + // +#ifdef DATABASE_PGSQL + { + odb::connection_ptr c (db->connection ()); + int v (static_cast<odb::pgsql::connection&> (*c).server_version ()); + if (v < 90400) + assert (db->schema_version () == 0); + } +#endif + + { + transaction t (db->begin ()); + assert (db->schema_version () == 0); + + schema_catalog::create_schema (*db, "", false); + + assert (db->schema_version () == 1 && !db->schema_migration ()); + + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + assert (db->schema_version () == 2 && !db->schema_migration ()); + + { + transaction t (db->begin ()); + object1 o1 (1); + o1.num = 123; + db->persist (o1); + t.commit (); + } + break; + } + case 2: + { + using namespace v2; + using namespace v3; + + // Check version information correctness. + // + assert (schema_catalog::base_version (*db) == 1); + assert (schema_catalog::current_version (*db) == 3); + assert (schema_catalog::next_version (*db, 0) == 3); + assert (schema_catalog::next_version (*db, 1) == 2); + assert (schema_catalog::next_version (*db) == 3); + assert (schema_catalog::next_version (*db, 3) == 4); + + { + assert (db->schema_version () == 2 && !db->schema_migration ()); + + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + assert (db->schema_version () == 3 && db->schema_migration ()); + + { + transaction t (db->begin ()); + unique_ptr<object1> o1 (db->load<object1> (1)); + object2 o2 (1); + o2.num = o1->num; + db->persist (o2); + t.commit (); + } + + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + + assert (db->schema_version () == 3 && !db->schema_migration ()); + } + break; + } + case 3: + { + using namespace v3; + + // In transaction. + // + { + transaction t (db->begin ()); + assert (db->schema_version () == 3 && !db->schema_migration ()); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> o2 (db->load<object2> (1)); + assert (o2->num == 123); + t.commit (); + } + + // Test the case where there is still no version table. + // + db->schema_version_migration (0, false); + + { + transaction t (db->begin ()); + +#ifdef DATABASE_ORACLE + db->execute ("DROP TABLE \"evo_embedded_sv\""); +#else + db->execute ("DROP TABLE evo_embedded_sv"); +#endif + t.commit (); + } + + assert (db->schema_version () == 0); + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/embedded/model.hxx b/odb-tests/evolution/embedded/model.hxx new file mode 100644 index 0000000..f3aa7a4 --- /dev/null +++ b/odb-tests/evolution/embedded/model.hxx @@ -0,0 +1,45 @@ +// file : evolution/embedded/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ +#if MODEL_VERSION == 2 + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif + +#if MODEL_VERSION == 3 + #pragma db object + struct object2 + { + object2 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/embedded/test1.hxx b/odb-tests/evolution/embedded/test1.hxx new file mode 100644 index 0000000..32903a1 --- /dev/null +++ b/odb-tests/evolution/embedded/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/embedded/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/embedded/test2.hxx b/odb-tests/evolution/embedded/test2.hxx new file mode 100644 index 0000000..fce8760 --- /dev/null +++ b/odb-tests/evolution/embedded/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/embedded/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/embedded/test3.hxx b/odb-tests/evolution/embedded/test3.hxx new file mode 100644 index 0000000..d49ecc5 --- /dev/null +++ b/odb-tests/evolution/embedded/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/embedded/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/embedded/testscript b/odb-tests/evolution/embedded/testscript new file mode 100644 index 0000000..3ddf983 --- /dev/null +++ b/odb-tests/evolution/embedded/testscript @@ -0,0 +1,21 @@ +# file : evolution/embedded/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + $* 1; + $* 2; + $* 3 +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/soft-add/buildfile b/odb-tests/evolution/soft-add/buildfile new file mode 100644 index 0000000..6a61697 --- /dev/null +++ b/odb-tests/evolution/soft-add/buildfile @@ -0,0 +1,64 @@ +# file : evolution/soft-add/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_soft_a_ \ + --schema-version-table evo_soft_a_sv \ + --generate-schema \ + --generate-query \ + --generate-prepared \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/soft-add/driver.cxx b/odb-tests/evolution/soft-add/driver.cxx new file mode 100644 index 0000000..3bd5f1f --- /dev/null +++ b/odb-tests/evolution/soft-add/driver.cxx @@ -0,0 +1,2224 @@ +// file : evolution/soft-add/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test soft-add functionality. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_soft_a_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v3; // NOTE: not v2. + + if (embedded) + { + // SQLite has broken foreign keys when it comes to DDL. + // +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=OFF"); +#endif + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=ON"); +#endif + } + + // Test basic soft-added member logic. + // + { + using namespace test2; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + o.ptr = new object1 (1); + o.ptr->ptrs.push_back (&o); + + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "" && p->num == 123 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->str == "" && i->num == 123 && + i->vec.empty () && i->ptr == 0); + + try + { + db->query<object> (query::str.is_null ()); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + o.ptr = new object1 (2); + o.ptr->ptrs.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 234 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + delete o.ptr; + o.ptr = 0; + + { + transaction t (db->begin ()); + db->erase<object1> (2); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 235 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test empty statement handling (INSERT, UPDATE). + // + { + using namespace test3; + + // None of the database operations should yet include the + // added members. + // + object o; + o.str = "bcd"; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == ""); + t.commit (); + } + + o.str += 'e'; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == ""); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test empty statement handling (SELECT in polymorphic loader). + // + { + using namespace test4; + + // None of the database operations should yet include the + // added members. + // + object o (1); + o.str = "abc"; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<base> p (db->load<base> (1)); + assert (static_cast<object&> (*p).str == ""); + db->erase (o); + t.commit (); + } + } + + // Test container with soft-added value member. + // + { + using namespace test5; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.vec.push_back (value ("abc", 123)); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->vec[0].str == "" && p->vec[0].num == 123); + t.commit (); + } + + object o (2); + o.vec.push_back (value ("bcd", 234)); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "" && p->vec[0].num == 234); + t.commit (); + } + + o.vec.modify (0).str += 'e'; + o.vec.modify (0).num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "" && p->vec[0].num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test view with soft-added member. + // + { + using namespace test6; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.str = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + result r (db->query<view> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + } + + // Test soft-added section member. + // + { + using namespace test7; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + try + { + db->load (*p, p->s); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-added members of a section. + // + { + using namespace test8; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "" && i->num == 123 && i->vec.empty ()); + + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // No longer automatically marked as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test basic soft-added member logic in polymorphic classes. + // + { + using namespace test9; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (static_cast<object*> (db->load<base> (1))); + assert (p->bstr == "" && p->dstr == "" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::id == 1)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + + try + { + db->query<base> (query::bstr == "ab"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->bstr == "" && i->dstr == "" && i->num); + + try + { + db->query<object> (query::dstr == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "" && p->dstr == "" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "" && p->dstr == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + } + + // Test soft-added section member in polymorphic classes. + // + { + using namespace test10; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + + try + { + db->load (*p, p->s); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + } + + // Test soft-added members of a section in polymorphic classes. + // + { + using namespace test11; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::id == 1)); + result::iterator i (r.begin ()); + db->load (*i, i->s); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + + try + { + db->query<base> (query::bstr == "ab"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + db->load (*i, i->s); + assert (i != r.end () && + i->bstr == "" && i->dstr == "" && i->num); + + try + { + db->query<object> (query::dstr == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "" && p->dstr == "" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "" && p->dstr == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + + // Test empty statement detection in sections. + // + base b (3); + b.bstr = "bc"; + + { + transaction t (db->begin ()); + db->persist (b); + unique_ptr<base> p (db->load<base> (3)); + db->load (*p, p->s); + assert (p->bstr == ""); + t.commit (); + } + + b.bstr += 'd'; + b.s.change (); + + { + transaction t (db->begin ()); + db->update (b); + unique_ptr<base> p (db->load<base> (3)); + db->load (*p, p->s); + assert (p->bstr == ""); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (b); + t.commit (); + } + } + + // Test soft-added member and optimistic concurrency. + // + { + using namespace test12; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.str = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + unsigned long long v (o.v_); + db->update (o); + assert (o.v_ != v); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 235 && p->v_ == o.v_); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-added member in an object without id. + // + { + using namespace test13; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // None of the database operations should yet include the + // added members. + // + { + object o; + o.str = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + + result r (db->query<object> (query::num == 234)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 234); + + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase_query<object> (query::num == 234); + t.commit (); + } + } + + // Test soft-added member in an object with auto id. + // + { + using namespace test14; + + // None of the database operations should yet include the + // added members. + // + unsigned long id; + { + object o; + o.str = "abc"; + o.num = 123; + + transaction t (db->begin ()); + db->persist (o); + id = o.id; + t.commit (); + } + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (id)); + assert (p->str == "" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} + + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (o.id); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned object. + // + { + using namespace test21; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.num = 123; + o.vec.push_back (123); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->num == 123 && i->vec.empty ()); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned section. + // + { + using namespace test22; + + // None of the database operations should yet include the + // added members. + // + { + object o (1); + o.num = 123; + o.vec.push_back (123); + + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->num == 123 && i->vec.empty ()); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; // No longer automatically marks as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Test basic soft-added member logic. + // + { + using namespace test2; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->str = "abc"; + p->vec.push_back (123); + delete p->ptr; + p->ptr = new object1 (1); + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123 && + p->vec[0] == 123 && p->ptr->id_ == 1); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str.is_not_null () && + query::ptr->id == 1)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->str == "abc" && i->num == 123 && + i->vec[0] == 123 && i->ptr->id_ == 1); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + o.ptr = new object1 (2); + o.ptr->ptrs.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcd" && p->num == 234 && + p->vec[0] == 234 && p->ptr->id_ == 2); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + delete o.ptr; + o.ptr = 0; + + { + transaction t (db->begin ()); + db->erase<object1> (2); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcde" && p->num == 235 && + p->vec[0] == 235 && p->ptr == 0); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test container with soft-added value member. + // + { + using namespace test5; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->vec.modify (0).str = "abc"; + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->vec[0].str == "abc" && p->vec[0].num == 123); + t.commit (); + } + + object o (2); + o.vec.push_back (value ("bcd", 234)); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "bcd" && p->vec[0].num == 234); + t.commit (); + } + + o.vec.modify (0).str += 'e'; + o.vec.modify (0).num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "bcde" && p->vec[0].num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test view with soft-added member. + // + { + using namespace test6; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->str = "abc"; + db->update (*p); + t.commit (); + } + + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + result r (db->query<view> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + } + + // Test soft-added section member. + // + { + using namespace test7; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + p->str = "abc"; + p->vec.push_back (123); + db->update (*p, p->s); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // Automatically marks section as updated. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-added members of a section. + // + { + using namespace test8; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + p->str = "abc"; + p->vec.push_back (123); + db->update (*p, p->s); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // Automatically marks section as updated. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test basic soft-added member logic in polymorphic classes. + // + { + using namespace test9; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->bstr = "ab"; + p->dstr = "abc"; + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (static_cast<object*> (db->load<base> (1))); + assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-added section member in polymorphic classes. + // + { + using namespace test10; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + p->bstr = "ab"; + p->dstr = "abc"; + db->update (*p, p->s); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-added members of a section in polymorphic classes. + // + { + using namespace test11; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + p->bstr = "ab"; + p->dstr = "abc"; + db->update (*p, p->s); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-added member and optimistic concurrency. + // + { + using namespace test12; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->str = "abc"; + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcd" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + unsigned long long v (o.v_); + db->update (o); + assert (o.v_ != v); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcde" && p->num == 235 && p->v_ == o.v_); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-added member in an object without id. + // + { + using namespace test13; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + + result r (db->query<object> (query::str == "bcd")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "bcd" && i->num == 234); + + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase_query<object> (query::str == "bcd"); + t.commit (); + } + } + + // Test soft-added member in an object with auto id. + // + { + using namespace test14; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // All the database operations should now include the added + // members. + // + unsigned long id; + { + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + i->str = "abc"; + db->update (*i); + id = i->id; + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (id)); + assert (p->str == "abc" && p->num == 123); + t.commit (); + } + + { + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "bcd" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned object. + // + { + using namespace test21; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + p->vec.push_back (123); + db->update (*p); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned section. + // + { + using namespace test22; + + // All the database operations should now include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + p->vec.push_back (123); + db->update (*p, p->s); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; // Automatically marks section as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + // Test basic soft-added member logic. + // + { + using namespace test2; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123 && + p->vec[0] == 123 && p->ptr->id_ == 1); + t.commit (); + } + } + + // Test container with soft-added value member. + // + { + using namespace test5; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->vec[0].str == "abc" && p->vec[0].num == 123); + t.commit (); + } + } + + // Test view with soft-added member. + // + { + using namespace test6; + + // All the database operations should still include the added + // members. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + result r (db->query<view> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + } + + // Test soft-added section member. + // + { + using namespace test7; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + } + + // Test soft-added members of a section. + // + { + using namespace test8; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + } + + // Test basic soft-added member logic in polymorphic classes. + // + { + using namespace test9; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (static_cast<object*> (db->load<base> (1))); + assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123); + t.commit (); + } + } + + // Test soft-added section member in polymorphic classes. + // + { + using namespace test10; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + } + + // Test soft-added members of a section in polymorphic classes. + // + { + using namespace test11; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + } + + // Test soft-added member and optimistic concurrency. + // + { + using namespace test12; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123); + t.commit (); + } + } + + // Test soft-added member in an object without id. + // + { + using namespace test13; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + } + + // Test soft-added member in an object with auto id. + // + { + using namespace test14; + + // All the database operations should still include the added + // members. + // + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned object. + // + { + using namespace test21; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + } + + // Test soft-added container member in a non-versioned section. + // + { + using namespace test22; + + // All the database operations should still include the added + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/soft-add/model.hxx b/odb-tests/evolution/soft-add/model.hxx new file mode 100644 index 0000000..39d63c4 --- /dev/null +++ b/odb-tests/evolution/soft-add/model.hxx @@ -0,0 +1,504 @@ +// file : evolution/soft-add/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> + +#include <odb/core.hxx> +#include <odb/vector.hxx> +#include <odb/section.hxx> +#include <odb/lazy-ptr.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + // The test numbers are made to correspond to the soft-delete ones. + // + + // Test basic soft-added member logic. + // + #pragma db namespace table("t2_") + namespace test2 + { + struct object; + + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<odb::lazy_ptr<object> > ptrs; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id), ptr (0) {} + ~object () {delete ptr;} + + #pragma db id + unsigned long id_; + + std::string str; + unsigned long num; + odb::vector<int> vec; + + #pragma db inverse(ptrs) + object1* ptr; + }; + +#if MODEL_VERSION == 3 + // Make it a LOB for Oracle and long data for SQL Server. + // + #pragma db member(object::str) added(3) \ + oracle:type("CLOB") \ + mssql:type("VARCHAR(max)") + #pragma db member(object::vec) added(3) + #pragma db member(object::ptr) added(3) +#else + #pragma db member(object::str) transient + #pragma db member(object::vec) transient + #pragma db member(object::ptr) transient +#endif + } + + // Test empty statement handling (INSERT, UPDATE). + // + #pragma db namespace table("t3_") + namespace test3 + { + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) +#else + #pragma db member(object::str) transient +#endif + } + + // Test empty statement handling (SELECT in polymorphic loader). + // + #pragma db namespace table("t4_") + namespace test4 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) +#else + #pragma db member(object::str) transient +#endif + } + + // Test container with soft-added value member. + // + #pragma db namespace table("t5_") + namespace test5 + { + #pragma db value + struct value + { + value () {} + value (const std::string& s, unsigned long n): str (s), num (n) {} + + std::string str; + unsigned long num; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<value> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(value::str) added(3) +#else + #pragma db member(value::str) transient +#endif + } + + // Test view with soft-added member. + // + #pragma db namespace table("t6_") + namespace test6 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string str; + unsigned long num; + }; + + #pragma db view object(object) + struct view + { + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) + #pragma db member(view::str) added(3) +#else + #pragma db member(object::str) transient + #pragma db member(view::str) transient +#endif + } + + // Test soft-added section member. + // + #pragma db namespace table("t7_") + namespace test7 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 3 + #pragma db load(lazy) update(change) added(3) + odb::section s; +#endif + + #pragma db section(s) + std::string str; + + unsigned long num; + + #pragma db section(s) + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) section(s) + #pragma db member(object::vec) section(s) +#else + #pragma db member(object::str) transient + #pragma db member(object::vec) transient +#endif + } + + // Test soft-added members of a section. + // + #pragma db namespace table("t8_") + namespace test8 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + std::string str; + + #pragma db section(s) + unsigned long num; + + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) section(s) + #pragma db member(object::vec) added(3) section(s) +#else + #pragma db member(object::str) transient + #pragma db member(object::vec) transient +#endif + } + + // Test basic soft-added member logic in polymorphic classes. + // + #pragma db namespace table("t9_") + namespace test9 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string dstr; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::bstr) added(3) + #pragma db member(object::dstr) added(3) +#else + #pragma db member(base::bstr) transient + #pragma db member(object::dstr) transient +#endif + } + + // Test soft-added section member in polymorphic classes. + // + #pragma db namespace table("t10_") + namespace test10 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + +#if MODEL_VERSION == 3 + #pragma db load(lazy) update(change) added(3) + odb::section s; +#endif + + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string dstr; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::bstr) section(s) + #pragma db member(object::dstr) section(s) +#else + #pragma db member(base::bstr) transient + #pragma db member(object::dstr) transient +#endif + } + + // Test soft-added members of a section in polymorphic classes. + // + #pragma db namespace table("t11_") + namespace test11 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string dstr; + + #pragma db section(s) + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::bstr) added(3) section(s) + #pragma db member(object::dstr) added(3) section(s) +#else + #pragma db member(base::bstr) transient + #pragma db member(object::dstr) transient +#endif + } + + // Test soft-added member and optimistic concurrency. + // + #pragma db namespace table("t12_") + namespace test12 + { + #pragma db object optimistic + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db version mssql:type("ROWVERSION") + unsigned long long v_; + + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) +#else + #pragma db member(object::str) transient +#endif + } + + // Test soft-added member in an object without id. + // + #pragma db namespace table("t13_") + namespace test13 + { + #pragma db object no_id + struct object + { + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) default("abc") \ + mysql:type("VARCHAR(255)") +#else + #pragma db member(object::str) transient +#endif + } + + // Test soft-added member in an object with auto id. + // + #pragma db namespace table("t14_") + namespace test14 + { + #pragma db object + struct object + { + std::string str; + unsigned long num; + + #pragma db id auto + unsigned long id; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) added(3) +#else + #pragma db member(object::str) transient +#endif + } + + // Test soft-added container member in a non-versioned object. + // + #pragma db namespace table("t21_") + namespace test21 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + unsigned long num; + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::vec) added(3) +#else + #pragma db member(object::vec) transient +#endif + } + + // Test soft-added container member in a non-versioned section. + // + #pragma db namespace table("t22_") + namespace test22 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + unsigned long num; + + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::vec) added(3) section(s) +#else + #pragma db member(object::vec) transient +#endif + } +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/soft-add/test1.hxx b/odb-tests/evolution/soft-add/test1.hxx new file mode 100644 index 0000000..461d663 --- /dev/null +++ b/odb-tests/evolution/soft-add/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/soft-add/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/soft-add/test2.hxx b/odb-tests/evolution/soft-add/test2.hxx new file mode 100644 index 0000000..746da4b --- /dev/null +++ b/odb-tests/evolution/soft-add/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/soft-add/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/soft-add/test3.hxx b/odb-tests/evolution/soft-add/test3.hxx new file mode 100644 index 0000000..f2990d0 --- /dev/null +++ b/odb-tests/evolution/soft-add/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/soft-add/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/soft-add/testscript b/odb-tests/evolution/soft-add/testscript new file mode 100644 index 0000000..5c26645 --- /dev/null +++ b/odb-tests/evolution/soft-add/testscript @@ -0,0 +1,58 @@ +# file : evolution/soft-add/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/soft-delete/buildfile b/odb-tests/evolution/soft-delete/buildfile new file mode 100644 index 0000000..1d98505 --- /dev/null +++ b/odb-tests/evolution/soft-delete/buildfile @@ -0,0 +1,65 @@ +# file : evolution/soft-delete/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_soft_d_ \ + --schema-version-table evo_soft_d_sv \ + --generate-schema \ + --generate-query \ + --generate-prepared \ + --at-once \ + --changelog $out_base/model.xml \ + --sqlite-override-null + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/soft-delete/driver.cxx b/odb-tests/evolution/soft-delete/driver.cxx new file mode 100644 index 0000000..edc5db6 --- /dev/null +++ b/odb-tests/evolution/soft-delete/driver.cxx @@ -0,0 +1,2207 @@ +// file : evolution/soft-delete/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test soft-delete functionality. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_soft_d_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + // SQLite has broken foreign keys when it comes to DDL. + // +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=OFF"); +#endif + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + schema_catalog::create_schema (*db, "", false); + schema_catalog::migrate_schema (*db, 2); + t.commit (); + +#ifdef DATABASE_SQLITE + db->connection ()->execute ("PRAGMA foreign_keys=ON"); +#endif + } + + // Test soft-deleted objects. + // + { + using namespace test1; + + object o (1); + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test basic soft-deleted member logic. + // + { + using namespace test2; + + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + o.ptr = new object1 (1); + o.ptr->ptrs.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + t.commit (); + } + } + + // Test container with soft-deleted value member. + // + { + using namespace test5; + + object o (1); + o.vec.push_back (value ("abc", 123)); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test view with soft-deleted member. + // + { + using namespace test6; + + object o (1); + o.str = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted section member. + // + { + using namespace test7; + + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted members of a section. + // + { + using namespace test8; + + object o (1); + o.str = "abc"; + o.num = 123; + o.vec.push_back (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test basic soft-deleted member logic in polymorphic classes. + // + { + // We have to use v3 here because the discriminator includes + // the namespace. + // + using namespace v3::test9; + + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted section member in polymorphic classes. + // + { + // We have to use v3 here because the discriminator includes + // the namespace. + // + using namespace v3::test10; + + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted members of a section in polymorphic classes. + // + { + // We have to use v3 here because the discriminator includes + // the namespace. + // + using namespace v3::test11; + + object o (1); + o.bstr = "ab"; + o.dstr = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted member and optimistic concurrency. + // + { + using namespace test12; + + object o (1); + o.str = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted member in an object without id. + // + { + using namespace test13; + + object o; + o.str = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted member in an object with auto id. + // + { + using namespace test14; + + object o; + o.str = "abc"; + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test summarily deleted composite values. + // + { + using namespace test15; + + object o (1); + o.v.reset (new value); + o.v->str = "abc"; + o.v->vec.push_back (123); + o.num = 123; + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned object. + // + { + using namespace test21; + + object o (1); + o.num = 123; + o.vec.push_back (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned section. + // + { + using namespace test22; + + object o (1); + o.num = 123; + o.vec.push_back (123); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + } + + break; + } + case 2: + { + using namespace v3; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + // Test soft-deleted objects. + // + { + using namespace test1; + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123); + t.commit (); + } + } + + // Test basic soft-deleted member logic. + // + { + using namespace test2; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123 && + p->vec[0] == 123 && p->ptr->id_ == 1); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc" && + query::ptr->id == 1)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->str == "abc" && i->num == 123 && + i->vec[0] == 123 && i->ptr->id_ == 1); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + o.ptr = new object1 (2); + o.ptr->ptrs.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcd" && p->num == 234 && + p->vec[0] == 234 && p->ptr->id_ == 2); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + delete o.ptr; + o.ptr = 0; + + { + transaction t (db->begin ()); + db->erase<object1> (2); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcde" && p->num == 235 && + p->vec[0] == 235 && p->ptr == 0); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test container with soft-deleted value member. + // + { + using namespace test5; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->vec[0].str == "abc" && p->vec[0].num == 123); + t.commit (); + } + + object o (2); + o.vec.push_back (value ("bcd", 234)); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "bcd" && p->vec[0].num == 234); + t.commit (); + } + + o.vec.modify (0).str += 'e'; + o.vec.modify (0).num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "bcde" && p->vec[0].num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test view with soft-deleted member. + // + { + using namespace test6; + + // All the database operations should still include the deleted + // members. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + result r (db->query<view> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + } + + // Test soft-deleted section member. + // + { + using namespace test7; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // Automatically marks section as updated. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-deleted members of a section. + // + { + using namespace test8; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "abc" && p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "abc" && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcd" && p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // Automatically marks section as updated. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "bcde" && p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test basic soft-deleted member logic in polymorphic classes. + // + { + using namespace test9; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (static_cast<object*> (db->load<base> (1))); + assert (p->bstr == "ab" && p->dstr == "abc" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-deleted section member in polymorphic classes. + // + { + using namespace test10; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-deleted members of a section in polymorphic classes. + // + { + using namespace test11; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::bstr == "ab")); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "ab" && o.dstr == "abc" && o.num == 123); + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bc" && p->dstr == "bcd" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "bcd" && p->dstr == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (static_cast<base&> (o)); + t.commit (); + } + } + + // Test soft-deleted member and optimistic concurrency. + // + { + using namespace test12; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "abc" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcd" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + unsigned long long v (o.v_); + db->update (o); + assert (o.v_ != v); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "bcde" && p->num == 235 && p->v_ == o.v_); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-deleted member in an object without id. + // + { + using namespace test13; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + + result r (db->query<object> (query::str == "bcd")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "bcd" && i->num == 234); + + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase_query<object> (query::str == "bcd"); + t.commit (); + } + } + + // Test soft-deleted member in an object with auto id. + // + { + using namespace test14; + + // All the database operations should still include the deleted + // members. + // + unsigned long id; + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "abc" && i->num == 123); + id = i->id; + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (id)); + assert (p->str == "abc" && p->num == 123); + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "bcd" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "bcde" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test summarily deleted composite values. + // + { + using namespace test15; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->v->str == "abc" && p->num == 123 && + p->v->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::v.str == "abc")); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->v->str == "abc" && i->num == 123 && + i->v->vec[0] == 123); + t.commit (); + } + + object o (2); + o.v.reset (new value); + o.v->str = "bcd"; + o.num = 234; + o.v->vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->v->str == "bcd" && p->num == 234 && + p->v->vec[0] == 234); + t.commit (); + } + + o.v->str += 'e'; + o.num++; + o.v->vec.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->v->str == "bcde" && p->num == 235 && + p->v->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned object. + // + { + using namespace test21; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned section. + // + { + using namespace test22; + + // All the database operations should still include the deleted + // members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->num == 123 && p->vec[0] == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->num == 123 && i->vec[0] == 123); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec[0] == 234); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; // Automatically marks section as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 235 && p->vec[0] == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase (o); + t.commit (); + } + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + } + break; + } + case 3: + { + using namespace v3; + + // Test soft-deleted objects. + // + { + using namespace test1; + + try + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); // No such table. + assert (false); + } + catch (const odb::exception&) {} + } + + // Test basic soft-deleted member logic. + // + { + using namespace test2; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "" && p->num == 123 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->str == "" && i->num == 123 && + i->vec.empty () && i->ptr == 0); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + o.ptr = new object1 (2); + o.ptr->ptrs.push_back (&o); + + { + transaction t (db->begin ()); + db->persist (o); + db->persist (*o.ptr); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 234 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + delete o.ptr; + o.ptr = 0; + + { + transaction t (db->begin ()); + db->erase<object1> (2); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 235 && + p->vec.empty () && p->ptr == 0); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test empty statement handling (INSERT, UPDATE). + // + { + using namespace test3; + + // Now none of the database operations should include the + // deleted member. + // + object o; + o.str = "bcd"; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == ""); + t.commit (); + } + + o.str += 'e'; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == ""); + t.commit (); + } + } + + // Test empty statement handling (SELECT in polymorphic loader). + // + { + using namespace test4; + + // Now none of the database operations should include the + // deleted member. + // + object o (1); + o.str = "abc"; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<base> p (db->load<base> (1)); + assert (static_cast<object&> (*p).str == ""); + t.commit (); + } + } + + // Test container with soft-deleted value member. + // + { + using namespace test5; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->vec[0].str == "" && p->vec[0].num == 123); + t.commit (); + } + + object o (2); + o.vec.push_back (value ("bcd", 234)); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "" && p->vec[0].num == 234); + t.commit (); + } + + o.vec.modify (0).str += 'e'; + o.vec.modify (0).num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->vec[0].str == "" && p->vec[0].num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test view with soft-deleted member. + // + { + using namespace test6; + + // Now none of the database operations should include the + // deleted members. + // + { + typedef odb::query<view> query; + typedef odb::result<view> result; + + transaction t (db->begin ()); + result r (db->query<view> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + } + + // Test soft-deleted section member. + // + { + using namespace test7; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->load (*p, p->s); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#endif + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-deleted members of a section. + // + { + using namespace test8; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->str == "" && i->num == 123 && i->vec.empty ()); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.str += 'e'; + o.num++; + o.vec.modify (0)++; // No longer automatically marked as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->str == "" && p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test basic soft-deleted member logic in polymorphic classes. + // + { + using namespace test9; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (static_cast<object*> (db->load<base> (1))); + assert (p->bstr == "" && p->dstr == "" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::id == 1)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<base> (query::bstr == "ab"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<base> (query::bstr.is_null ())) == 1); +#endif + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && + i->bstr == "" && i->dstr == "" && i->num); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::dstr == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::dstr.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "" && p->dstr == "" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + assert (p->bstr == "" && p->dstr == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + } + + // Test soft-deleted section member in polymorphic classes. + // + { + using namespace test10; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->load (*p, p->s); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#endif + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + } + + // Test soft-deleted members of a section in polymorphic classes. + // + { + using namespace test11; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<base> p (db->load<base> (1)); + db->load (*p, p->s); + object& o (static_cast<object&> (*p)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + t.commit (); + } + + { + typedef odb::query<base> query; + typedef odb::result<base> result; + + transaction t (db->begin ()); + result r (db->query<base> (query::id == 1)); + result::iterator i (r.begin ()); + db->load (*i, i->s); + assert (i != r.end ()); + object& o (static_cast<object&> (*i)); + assert (o.bstr == "" && o.dstr == "" && o.num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<base> (query::bstr == "ab"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<base> (query::bstr.is_null ())) == 1); +#endif + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + db->load (*i, i->s); + assert (i != r.end () && + i->bstr == "" && i->dstr == "" && i->num); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::dstr == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::dstr.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.bstr = "bc"; + o.dstr = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "" && p->dstr == "" && p->num == 234); + t.commit (); + } + + o.bstr += 'd'; + o.dstr += 'e'; + o.num++; + o.s.change (); + + { + transaction t (db->begin ()); + db->update (static_cast<base&> (o)); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->bstr == "" && p->dstr == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<base> (2); + t.commit (); + } + + // Test empty statement detection in sections. + // + base b (3); + b.bstr = "bc"; + + { + transaction t (db->begin ()); + db->persist (b); + unique_ptr<base> p (db->load<base> (3)); + db->load (*p, p->s); + assert (p->bstr == ""); + t.commit (); + } + + b.bstr += 'd'; + b.s.change (); + + { + transaction t (db->begin ()); + db->update (b); + unique_ptr<base> p (db->load<base> (3)); + db->load (*p, p->s); + assert (p->bstr == ""); + t.commit (); + } + } + + // Test soft-deleted member and optimistic concurrency. + // + { + using namespace test12; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->str == "" && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + unsigned long long v (o.v_); + db->update (o); + assert (o.v_ != v); + unique_ptr<object> p (db->load<object> (2)); + assert (p->str == "" && p->num == 235 && p->v_ == o.v_); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-deleted member in an object without id. + // + { + using namespace test13; + + typedef odb::query<object> query; + typedef odb::result<object> result; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + + result r (db->query<object> (query::num == 234)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 234); + + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase_query<object> (query::num == 234); + t.commit (); + } + } + + // Test soft-deleted member in an object with auto id. + // + { + using namespace test14; + + // Now none of the database operations should include the + // deleted members. + // + unsigned long id; + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->str == "" && i->num == 123); + id = i->id; + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::str.is_null ())) == 1); +#endif + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (id)); + assert (p->str == "" && p->num == 123); + t.commit (); + } + + object o; + o.str = "bcd"; + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "" && p->num == 234); + t.commit (); + } + + o.str += 'e'; + o.num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (o.id)); + assert (p->str == "" && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (o.id); + t.commit (); + } + } + + // Test summarily deleted composite values. + // + { + using namespace test15; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->v.get () == 0 && p->num == 123); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->v.get () == 0 && i->num == 123); + + // Logical delete in SQLite. + // +#ifndef DATABASE_SQLITE + try + { + db->query<object> (query::v.str == "abc"); // No such column. + assert (false); + } + catch (const odb::exception&) {} +#else + assert (size (db->query<object> (query::v.str.is_null ())) == 1); +#endif + t.commit (); + } + + object o (2); + o.num = 234; + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->v.get () == 0 && p->num == 234); + t.commit (); + } + + o.num++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->v.get () == 0 && p->num == 235); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned object. + // + { + using namespace test21; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + assert (p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end () && i->num == 123 && i->vec.empty ()); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + assert (p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + // Test soft-deleted container member in a non-versioned section. + // + { + using namespace test22; + + // Now none of the database operations should include the + // deleted members. + // + { + transaction t (db->begin ()); + unique_ptr<object> p (db->load<object> (1)); + db->load (*p, p->s); + assert (p->num == 123 && p->vec.empty ()); + t.commit (); + } + + { + typedef odb::query<object> query; + typedef odb::result<object> result; + + transaction t (db->begin ()); + result r (db->query<object> (query::num == 123)); + result::iterator i (r.begin ()); + assert (i != r.end ()); + db->load (*i, i->s); + assert (i->num == 123 && i->vec.empty ()); + t.commit (); + } + + object o (2); + o.num = 234; + o.vec.push_back (234); + + { + transaction t (db->begin ()); + db->persist (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.num++; + o.vec.modify (0)++; // No longer automatically marks as changed. + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 234 && p->vec.empty ()); + t.commit (); + } + + o.s.change (); + + { + transaction t (db->begin ()); + db->update (o); + unique_ptr<object> p (db->load<object> (2)); + db->load (*p, p->s); + assert (p->num == 235 && p->vec.empty ()); + t.commit (); + } + + { + transaction t (db->begin ()); + db->erase<object> (2); + t.commit (); + } + } + + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/soft-delete/model.hxx b/odb-tests/evolution/soft-delete/model.hxx new file mode 100644 index 0000000..093aadf --- /dev/null +++ b/odb-tests/evolution/soft-delete/model.hxx @@ -0,0 +1,512 @@ +// file : evolution/soft-delete/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <string> +#include <memory> // unique_ptr + +#include <odb/core.hxx> +#include <odb/vector.hxx> +#include <odb/section.hxx> +#include <odb/lazy-ptr.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ + // Test soft-deleted objects. + // + #pragma db namespace table("t1_") + namespace test1 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db object(object) deleted(3) +#endif + } + + // Test basic soft-deleted member logic. + // + #pragma db namespace table("t2_") + namespace test2 + { + struct object; + + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<odb::lazy_ptr<object> > ptrs; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id), ptr (0) {} + ~object () {delete ptr;} + + #pragma db id + unsigned long id_; + + std::string str; + unsigned long num; + odb::vector<int> vec; + + #pragma db inverse(ptrs) + object1* ptr; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) + #pragma db member(object::vec) deleted(3) + #pragma db member(object::ptr) deleted(3) +#endif + } + + // Test empty statement handling (INSERT, UPDATE). + // + #pragma db namespace table("t3_") + namespace test3 + { + #pragma db object + struct object + { + #pragma db id auto + unsigned long id; + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) +#endif + } + + // Test empty statement handling (SELECT in polymorphic loader). + // + #pragma db namespace table("t4_") + namespace test4 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string str; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) +#endif + } + + // Test container with soft-deleted value member. + // + #pragma db namespace table("t5_") + namespace test5 + { + #pragma db value + struct value + { + value () {} + value (const std::string& s, unsigned long n): str (s), num (n) {} + + std::string str; + unsigned long num; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + odb::vector<value> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(value::str) deleted(3) +#endif + } + + // Test view with soft-deleted member. + // + #pragma db namespace table("t6_") + namespace test6 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string str; + unsigned long num; + }; + + #pragma db view object(object) + struct view + { + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) + #pragma db member(view::str) deleted(3) +#endif + } + + // Test soft-deleted section member. + // + #pragma db namespace table("t7_") + namespace test7 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + std::string str; + + unsigned long num; + + #pragma db section(s) + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::s) deleted(3) +#endif + } + + // Test soft-deleted members of a section. + // + #pragma db namespace table("t8_") + namespace test8 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + std::string str; + + #pragma db section(s) + unsigned long num; + + #pragma db section(s) + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) + #pragma db member(object::vec) deleted(3) +#endif + } + + // Test basic soft-deleted member logic in polymorphic classes. + // + #pragma db namespace table("t9_") + namespace test9 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + std::string dstr; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::bstr) deleted(3) + #pragma db member(object::dstr) deleted(3) +#endif + } + + // Test soft-deleted section member in polymorphic classes. + // + #pragma db namespace table("t10_") + namespace test10 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + #pragma db section(s) + std::string dstr; + + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::s) deleted(3) +#endif + } + + // Test soft-deleted members of a section in polymorphic classes. + // + #pragma db namespace table("t11_") + namespace test11 + { + #pragma db object polymorphic + struct base + { + virtual + ~base () {} + base (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + std::string bstr; + }; + + #pragma db object + struct object: base + { + object (unsigned long id = 0): base (id) {} + + #pragma db section(s) + std::string dstr; + + #pragma db section(s) + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(base::bstr) deleted(3) + #pragma db member(object::dstr) deleted(3) +#endif + } + + // Test soft-deleted member and optimistic concurrency. + // + #pragma db namespace table("t12_") + namespace test12 + { + #pragma db object optimistic + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db version mssql:type("ROWVERSION") + unsigned long long v_; + + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) +#endif + } + + // Test soft-deleted member in an object without id. + // + #pragma db namespace table("t13_") + namespace test13 + { + #pragma db object no_id + struct object + { + std::string str; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) +#endif + } + + // Test soft-deleted member in an object with auto id. + // + #pragma db namespace table("t14_") + namespace test14 + { + #pragma db object + struct object + { + std::string str; + unsigned long num; + + #pragma db id auto + unsigned long id; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::str) deleted(3) +#endif + } + + // Test summarily deleted composite values. + // + #pragma db namespace table("t15_") + namespace test15 + { + #pragma db value + struct value + { + std::string str; + odb::vector<int> vec; + }; + + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + std::unique_ptr<value> v; + unsigned long num; + }; + +#if MODEL_VERSION == 3 + #pragma db member(value::str) deleted(3) + #pragma db member(value::vec) deleted(3) +#endif + } + + // Test soft-deleted container member in a non-versioned object. + // + #pragma db namespace table("t21_") + namespace test21 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + unsigned long num; + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::vec) deleted(3) +#endif + } + + // Test soft-deleted container member in a non-versioned section. + // + #pragma db namespace table("t22_") + namespace test22 + { + #pragma db object + struct object + { + object (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + #pragma db load(lazy) update(change) + odb::section s; + + #pragma db section(s) + unsigned long num; + + #pragma db section(s) + odb::vector<int> vec; + }; + +#if MODEL_VERSION == 3 + #pragma db member(object::vec) deleted(3) +#endif + } +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/soft-delete/test1.hxx b/odb-tests/evolution/soft-delete/test1.hxx new file mode 100644 index 0000000..d4df90f --- /dev/null +++ b/odb-tests/evolution/soft-delete/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/soft-delete/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/soft-delete/test2.hxx b/odb-tests/evolution/soft-delete/test2.hxx new file mode 100644 index 0000000..3b2b5b4 --- /dev/null +++ b/odb-tests/evolution/soft-delete/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/soft-delete/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/soft-delete/test3.hxx b/odb-tests/evolution/soft-delete/test3.hxx new file mode 100644 index 0000000..5a90ab2 --- /dev/null +++ b/odb-tests/evolution/soft-delete/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/soft-delete/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/soft-delete/testscript b/odb-tests/evolution/soft-delete/testscript new file mode 100644 index 0000000..63e81e6 --- /dev/null +++ b/odb-tests/evolution/soft-delete/testscript @@ -0,0 +1,58 @@ +# file : evolution/soft-delete/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} diff --git a/odb-tests/evolution/version/buildfile b/odb-tests/evolution/version/buildfile new file mode 100644 index 0000000..f11559f --- /dev/null +++ b/odb-tests/evolution/version/buildfile @@ -0,0 +1,63 @@ +# file : evolution/version/buildfile +# license : GNU GPL v2; see accompanying LICENSE file + +if ($build.meta_operation != 'dist') + assert (!$multi) "multi-database mode is not supported by this test" + +db = ($databases[0]) + +import libodb = libodb%lib{odb} + +import libs = libodb-$db%lib{odb-$db} +import libs += lib{common} + +hdrs = test1 test2 test3 + +exe{driver}: {hxx cxx}{* -*-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{$h} libue{test-meta} hxx{model} + +# Make sure testN.hxx are compiled serially since they share the changelog. +# +# @@ TODO: make order-only when supported by build2. +# +{hxx ixx cxx}{test3-odb}: {hxx ixx cxx}{test2-odb}: {hxx ixx cxx}{test1-odb} + +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 evo_version_ \ + --schema-version-table evo_version_sv \ + --generate-schema \ + --generate-query \ + --at-once \ + --changelog $out_base/model.xml + +<{hxx ixx cxx}{test1-odb}>: odb_options += --init-changelog +<{hxx ixx cxx}{test2-odb}>: odb_options += --omit-create --suppress-migration + +<{hxx ixx cxx}{test3-odb}>: +{ + odb_options += --omit-create + schema_versions = 002 003 +} + +cxx.poptions =+ "-I$out_base" "-I$src_base" + +# Testscript's run-time prerequisites. +# +exe{driver}: ../../alias{database-client}: include = adhoc + +testscript@./: +{ + db = $db + schemas = $hdrs +} diff --git a/odb-tests/evolution/version/driver.cxx b/odb-tests/evolution/version/driver.cxx new file mode 100644 index 0000000..91aaeaa --- /dev/null +++ b/odb-tests/evolution/version/driver.cxx @@ -0,0 +1,161 @@ +// file : evolution/version/driver.cxx +// license : GNU GPL v2; see accompanying LICENSE file + +// Test schema version access via the database instance. +// + +#include <memory> // std::unique_ptr +#include <iostream> + +#include <odb/database.hxx> +#include <odb/transaction.hxx> +#include <odb/schema-catalog.hxx> + +#include <libcommon/config.hxx> // DATABASE_XXX +#include <libcommon/common.hxx> + +#include "test2.hxx" +#include "test3.hxx" +#include "test2-odb.hxx" +#include "test3-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)); + + db->schema_version_table (quote_name ("evo_version_sv")); + + bool embedded (schema_catalog::exists (*db)); + + // 1 - base version + // 2 - migration + // 3 - current version + // + unsigned short pass (*argv[argc - 1] - '0'); + + switch (pass) + { + case 1: + { + using namespace v2; + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::drop_schema (*db); + + assert (db->schema_version () == 0); + + schema_catalog::create_schema (*db, "", false); + + assert (db->schema_version () == 1 && !db->schema_migration ()); + + schema_catalog::migrate_schema (*db, 2); + t.commit (); + } + + assert (db->schema_version () == 2 && !db->schema_migration ()); + + { + transaction t (db->begin ()); + object1 o1 (1); + o1.num = 123; + db->persist (o1); + t.commit (); + } + break; + } + case 2: + { + using namespace v2; + using namespace v3; + + if (embedded) + { + assert (db->schema_version () == 2 && !db->schema_migration ()); + + transaction t (db->begin ()); + schema_catalog::migrate_schema_pre (*db, 3); + t.commit (); + } + + assert (db->schema_version () == 3 && db->schema_migration ()); + + { + transaction t (db->begin ()); + unique_ptr<object1> o1 (db->load<object1> (1)); + object2 o2 (1); + o2.num = o1->num; + db->persist (o2); + t.commit (); + } + + if (embedded) + { + transaction t (db->begin ()); + schema_catalog::migrate_schema_post (*db, 3); + t.commit (); + + assert (db->schema_version () == 3 && !db->schema_migration ()); + } + break; + } + case 3: + { + using namespace v3; + + // In transaction. + // + { + transaction t (db->begin ()); + assert (db->schema_version () == 3 && !db->schema_migration ()); + t.commit (); + } + + { + transaction t (db->begin ()); + unique_ptr<object2> o2 (db->load<object2> (1)); + assert (o2->num == 123); + t.commit (); + } + + // Test the case where there is still no version table. + // + db->schema_version_migration (0, false); + + { + transaction t (db->begin ()); + +#ifdef DATABASE_ORACLE + db->execute ("DROP TABLE \"evo_version_sv\""); +#else + db->execute ("DROP TABLE evo_version_sv"); +#endif + t.commit (); + } + + assert (db->schema_version () == 0); + break; + } + default: + { + cerr << "unknown pass number '" << argv[argc - 1] << "'" << endl; + return 1; + } + } + } + catch (const odb::exception& e) + { + cerr << e.what () << endl; + return 1; + } +} diff --git a/odb-tests/evolution/version/model.hxx b/odb-tests/evolution/version/model.hxx new file mode 100644 index 0000000..cdda00e --- /dev/null +++ b/odb-tests/evolution/version/model.hxx @@ -0,0 +1,45 @@ +// file : evolution/version/model.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef MODEL_VERSION +# error model.hxx included directly +#endif + +#include <odb/core.hxx> + +#pragma db model version(1, MODEL_VERSION) + +#define MODEL_NAMESPACE_IMPL(V) v##V +#define MODEL_NAMESPACE(V) MODEL_NAMESPACE_IMPL(V) + +namespace MODEL_NAMESPACE(MODEL_VERSION) +{ +#if MODEL_VERSION == 2 + #pragma db object + struct object1 + { + object1 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif + +#if MODEL_VERSION == 3 + #pragma db object + struct object2 + { + object2 (unsigned long id = 0): id_ (id) {} + + #pragma db id + unsigned long id_; + + int num; + }; +#endif +} + +#undef MODEL_NAMESPACE +#undef MODEL_NAMESPACE_IMPL diff --git a/odb-tests/evolution/version/test1.hxx b/odb-tests/evolution/version/test1.hxx new file mode 100644 index 0000000..a50e54c --- /dev/null +++ b/odb-tests/evolution/version/test1.hxx @@ -0,0 +1,9 @@ +// file : evolution/version/test1.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST1_HXX +#define TEST1_HXX + +#pragma db model version(1, 1) + +#endif // TEST1_HXX diff --git a/odb-tests/evolution/version/test2.hxx b/odb-tests/evolution/version/test2.hxx new file mode 100644 index 0000000..f7fc1b7 --- /dev/null +++ b/odb-tests/evolution/version/test2.hxx @@ -0,0 +1,11 @@ +// file : evolution/version/test2.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST2_HXX +#define TEST2_HXX + +#define MODEL_VERSION 2 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST2_HXX diff --git a/odb-tests/evolution/version/test3.hxx b/odb-tests/evolution/version/test3.hxx new file mode 100644 index 0000000..364ee31 --- /dev/null +++ b/odb-tests/evolution/version/test3.hxx @@ -0,0 +1,11 @@ +// file : evolution/version/test3.hxx +// license : GNU GPL v2; see accompanying LICENSE file + +#ifndef TEST3_HXX +#define TEST3_HXX + +#define MODEL_VERSION 3 +#include "model.hxx" +#undef MODEL_VERSION + +#endif // TEST3_HXX diff --git a/odb-tests/evolution/version/testscript b/odb-tests/evolution/version/testscript new file mode 100644 index 0000000..08290ae --- /dev/null +++ b/odb-tests/evolution/version/testscript @@ -0,0 +1,58 @@ +# file : evolution/version/testscript +# license : GNU GPL v2; see accompanying LICENSE file + +.include ../../database-options.testscript +.include ../../$db-schema.testscript + +test.arguments += $($(db)_options) + +: basics +: +if! $sqlite +{ + ss =; # Schema modification base file names. + + # Drop everything. + # + for s: $schemas + ss =+ $s + end; + + # Add base schema. + # + ss += test3-002-pre test3-002-post; + + # Add migration. + # + ss += test3-003-pre test3-003-post; + + # Run tests. + # + for s: $ss + f = $out_base/"$s".sql + + if $mysql + cat $f | $create_schema_cmd + elif $pgsql + $create_schema_cmd -f $f + elif $oracle + $create_schema_cmd "@$f" + elif $mssql + $create_schema_cmd -i $f + end + + if ($s == 'test3-002-post') + $* 1 + elif ($s == 'test3-003-pre') + $* 2 + elif ($s == 'test3-003-post') + $* 3 + end + end +} +else +{ + $* 1 &odb-test.db; + $* 2; + $* 3 +} |