diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2013-04-09 16:17:26 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2013-04-09 16:17:26 +0200 |
commit | 287a41287ed26bcf481f087680cad56ed5ef9865 (patch) | |
tree | a139f2da973f2e6576d2673e9e096a4e4ab12e6f | |
parent | 4962329eecb716bc2b99810ec1ac4214606fc1e8 (diff) |
Add support for embedded schema migration
-rw-r--r-- | odb/exceptions.cxx | 22 | ||||
-rw-r--r-- | odb/exceptions.hxx | 22 | ||||
-rw-r--r-- | odb/forward.hxx | 2 | ||||
-rw-r--r-- | odb/schema-catalog-impl.hxx | 17 | ||||
-rw-r--r-- | odb/schema-catalog.cxx | 116 | ||||
-rw-r--r-- | odb/schema-catalog.hxx | 74 |
6 files changed, 234 insertions, 19 deletions
diff --git a/odb/exceptions.cxx b/odb/exceptions.cxx index 9502f96..1ee17e5 100644 --- a/odb/exceptions.cxx +++ b/odb/exceptions.cxx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : GNU GPL v2; see accompanying LICENSE file +#include <sstream> #include <odb/exceptions.hxx> using namespace std; @@ -163,4 +164,25 @@ namespace odb { return what_.c_str (); } + + unknown_schema_version:: + unknown_schema_version (schema_version v) + : version_ (v) + { + ostringstream os; + os << v; + what_ = "unknown database schema version "; + what_ += os.str (); + } + + unknown_schema_version:: + ~unknown_schema_version () throw () + { + } + + const char* unknown_schema_version:: + what () const throw () + { + return what_.c_str (); + } } diff --git a/odb/exceptions.hxx b/odb/exceptions.hxx index e4d8292..3c1ac20 100644 --- a/odb/exceptions.hxx +++ b/odb/exceptions.hxx @@ -9,7 +9,7 @@ #include <string> -#include <odb/forward.hxx> // odb::core +#include <odb/forward.hxx> // schema_version, odb::core #include <odb/exception.hxx> #include <odb/details/export.hxx> @@ -189,6 +189,25 @@ namespace odb std::string what_; }; + struct LIBODB_EXPORT unknown_schema_version: exception + { + unknown_schema_version (schema_version); + ~unknown_schema_version () throw (); + + schema_version + version () const + { + return version_; + } + + virtual const char* + what () const throw (); + + private: + schema_version version_; + std::string what_; + }; + namespace common { using odb::null_pointer; @@ -215,6 +234,7 @@ namespace odb using odb::no_type_info; using odb::unknown_schema; + using odb::unknown_schema_version; } } diff --git a/odb/forward.hxx b/odb/forward.hxx index 3dd5ef8..a09f08d 100644 --- a/odb/forward.hxx +++ b/odb/forward.hxx @@ -27,6 +27,7 @@ namespace odb // // + typedef unsigned long long schema_version; class database; class connection; typedef details::shared_ptr<connection> connection_ptr; @@ -36,6 +37,7 @@ namespace odb namespace common { + using odb::schema_version; using odb::session; } diff --git a/odb/schema-catalog-impl.hxx b/odb/schema-catalog-impl.hxx index 414dadd..f14f1df 100644 --- a/odb/schema-catalog-impl.hxx +++ b/odb/schema-catalog-impl.hxx @@ -9,7 +9,7 @@ #include <cstddef> -#include <odb/forward.hxx> +#include <odb/forward.hxx> // schema_version #include <odb/details/export.hxx> @@ -32,13 +32,22 @@ namespace odb // Catalog entry registration. // - struct LIBODB_EXPORT schema_catalog_entry + struct LIBODB_EXPORT schema_catalog_create_entry { - schema_catalog_entry ( - database_id id, + schema_catalog_create_entry ( + database_id, const char* name, bool (*create_function) (database&, unsigned short pass, bool drop)); }; + + struct LIBODB_EXPORT schema_catalog_migrate_entry + { + schema_catalog_migrate_entry ( + database_id, + const char* name, + schema_version, + bool (*migrate_function) (database&, unsigned short pass, bool pre)); + }; } #include <odb/post.hxx> diff --git a/odb/schema-catalog.cxx b/odb/schema-catalog.cxx index 07793d8..52b612f 100644 --- a/odb/schema-catalog.cxx +++ b/odb/schema-catalog.cxx @@ -4,6 +4,7 @@ #include <map> #include <vector> +#include <cassert> #include <odb/database.hxx> #include <odb/exceptions.hxx> @@ -14,15 +15,21 @@ using namespace std; namespace odb { - // It is important we use vector to store the list of create - // functions since schema generators for some databases may - // rely on the DDL statements executing in a specific order, - // for example, for foreign key generation. - // typedef bool (*create_function) (database&, unsigned short pass, bool drop); + typedef bool (*migrate_function) (database&, unsigned short pass, bool pre); + typedef pair<database_id, string> key; typedef vector<create_function> create_functions; - struct schema_catalog_impl: map<key, create_functions> {}; + typedef vector<migrate_function> migrate_functions; + typedef map<schema_version, migrate_functions> version_map; + + struct schema_functions + { + create_functions create; + version_map migrate; + }; + + struct schema_catalog_impl: map<key, schema_functions> {}; schema_catalog_impl* schema_catalog_init::catalog = 0; size_t schema_catalog_init::count = 0; @@ -43,7 +50,7 @@ namespace odb if (i == c.end ()) throw unknown_schema (name); - const create_functions& fs (i->second); + const create_functions& fs (i->second.create); // Run the passes until we ran them all or all the functions // return false, which means no more passes necessary. Do that @@ -81,6 +88,79 @@ namespace odb } } + void schema_catalog:: + migrate_schema_impl (database& db, + schema_version v, + const string& name, + migrate_mode m) + { + const schema_catalog_impl& c (*schema_catalog_init::catalog); + schema_catalog_impl::const_iterator i (c.find (key (db.id (), name))); + + if (i == c.end ()) + throw unknown_schema (name); + + const version_map& vm (i->second.migrate); + version_map::const_iterator j (vm.find (v)); + + if (j == vm.end ()) + throw unknown_schema_version (v); + + const migrate_functions& fs (j->second); + + // Run the passes until we ran them all or all the functions + // return false, which means no more passes necessary. + // + for (bool pre (m != migrate_post);; pre = false) + { + for (unsigned short pass (1); pass < 3; ++pass) + { + bool done (true); + + for (migrate_functions::const_iterator i (fs.begin ()), e (fs.end ()); + i != e; ++i) + { + if ((*i) (db, pass, pre)) + done = false; + } + + if (done) + break; + } + + if (!pre || m != migrate_both) + break; + } + } + + schema_version schema_catalog:: + next_version (database_id id, schema_version current, const string& name) + { + const schema_catalog_impl& c (*schema_catalog_init::catalog); + schema_catalog_impl::const_iterator i (c.find (key (id, name))); + + if (i == c.end ()) + throw unknown_schema (name); + + const version_map& vm (i->second.migrate); + version_map::const_iterator j (vm.upper_bound (current)); + return j != vm.end () ? j->first : 0; + } + + schema_version schema_catalog:: + latest_version (database_id id, const string& name) + { + const schema_catalog_impl& c (*schema_catalog_init::catalog); + schema_catalog_impl::const_iterator i (c.find (key (id, name))); + + if (i == c.end ()) + throw unknown_schema (name); + + const version_map& vm (i->second.migrate); + assert (!vm.empty ()); + return vm.rbegin ()->first; + } + // schema_catalog_init // schema_catalog_init:: @@ -99,12 +179,26 @@ namespace odb delete catalog; } - // schema_catalog_entry + // schema_catalog_create_entry + // + schema_catalog_create_entry:: + schema_catalog_create_entry (database_id id, + const char* name, + create_function cf) + { + schema_catalog_impl& c (*schema_catalog_init::catalog); + c[key(id, name)].create.push_back (cf); + } + + // schema_catalog_migrate_entry // - schema_catalog_entry:: - schema_catalog_entry (database_id id, const char* name, create_function cf) + schema_catalog_migrate_entry:: + schema_catalog_migrate_entry (database_id id, + const char* name, + schema_version v, + migrate_function mf) { schema_catalog_impl& c (*schema_catalog_init::catalog); - c[key(id, name)].push_back (cf); + c[key(id, name)].migrate[v].push_back (mf); } } diff --git a/odb/schema-catalog.hxx b/odb/schema-catalog.hxx index b9252d3..da6b767 100644 --- a/odb/schema-catalog.hxx +++ b/odb/schema-catalog.hxx @@ -9,7 +9,7 @@ #include <string> -#include <odb/forward.hxx> // odb::core +#include <odb/forward.hxx> // schema_version, odb::core #include <odb/details/export.hxx> @@ -18,17 +18,85 @@ namespace odb class LIBODB_EXPORT schema_catalog { public: + // Schema creation. + // static void create_schema (database&, const std::string& name = ""); - static bool - exists (database_id, const std::string& name); + // Schema migration. + // + static void + migrate_schema_pre (database& db, + schema_version v, + const std::string& name = "") + { + migrate_schema_impl (db, v, name, migrate_pre); + } + + static void + migrate_schema_post (database& db, + schema_version v, + const std::string& name = "") + { + migrate_schema_impl (db, v, name, migrate_post); + } + + static void + migrate_schema (database& db, + schema_version v, + const std::string& name = "") + { + migrate_schema_impl (db, v, name, migrate_both); + } + + // Return 0 if current is greater or equal to the latest version. + // + schema_version + next_version (const database& db, + schema_version current /*= 0*/, + const std::string& name = "") + { + return next_version (db.id (), current, name); + } + + schema_version + next_version (database_id, + schema_version current /*= 0*/, + const std::string& name = ""); + + schema_version + latest_version (const database& db, const std::string& name = "") + { + return latest_version (db.id (), name); + } + + schema_version + latest_version (database_id, const std::string& name = ""); + // Test for presence of a schema with a specific name. + // static bool exists (const database& db, const std::string& name) { return exists (db.id (), name); } + + static bool + exists (database_id, const std::string& name); + + private: + enum migrate_mode + { + migrate_pre, + migrate_post, + migrate_both + }; + + static void + migrate_schema_impl (database&, + schema_version, + const std::string& name, + migrate_mode); }; namespace common |