From 429cd8f13191cc08f6461d646fdde2d573496f06 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 25 Apr 2013 10:46:33 +0200 Subject: Add support for data migration --- odb/schema-catalog.cxx | 122 ++++++++++++++++++++++++++++++++++++++++++------- odb/schema-catalog.hxx | 85 +++++++++++++++++++++++++++++++++- 2 files changed, 189 insertions(+), 18 deletions(-) diff --git a/odb/schema-catalog.cxx b/odb/schema-catalog.cxx index 137931f..fefb52c 100644 --- a/odb/schema-catalog.cxx +++ b/odb/schema-catalog.cxx @@ -15,6 +15,8 @@ using namespace std; namespace odb { + // Schema. + // typedef bool (*create_function) (database&, unsigned short pass, bool drop); typedef bool (*migrate_function) (database&, unsigned short pass, bool pre); @@ -28,26 +30,65 @@ namespace odb create_functions create; version_map migrate; }; + typedef map schema_map; + + // Data. Normally the code would be database-independent, though there + // could be database-specific migration steps. + // + typedef pair data_key; - struct schema_catalog_impl: map {}; + struct data_function + { + typedef schema_catalog::data_migration_function_type function_type; + + data_function () {} + data_function (database_id i, function_type m): id (i), migrate (m) {} + database_id id; + function_type migrate; + }; + typedef vector data_functions; + typedef map data_map; + + struct schema_catalog_impl + { + schema_map schema; + data_map data; + }; + + // Static initialization. + // schema_catalog_impl* schema_catalog_init::catalog = 0; size_t schema_catalog_init::count = 0; + struct schema_catalog_init_extra + { + bool initialized; + + schema_catalog_init_extra (): initialized (false) {} + ~schema_catalog_init_extra () + { + if (initialized && --schema_catalog_init::count == 0) + delete schema_catalog_init::catalog; + } + }; + + static schema_catalog_init_extra schema_catalog_init_extra_; + bool schema_catalog:: - exists (database_id id, const std::string& name) + exists (database_id id, const string& name) { const schema_catalog_impl& c (*schema_catalog_init::catalog); - return c.find (key (id, name)) != c.end (); + return c.schema.find (key (id, name)) != c.schema.end (); } void schema_catalog:: create_schema (database& db, const string& name, bool drop) { const schema_catalog_impl& c (*schema_catalog_init::catalog); - schema_catalog_impl::const_iterator i (c.find (key (db.id (), name))); + schema_map::const_iterator i (c.schema.find (key (db.id (), name))); - if (i == c.end ()) + if (i == c.schema.end ()) throw unknown_schema (name); const create_functions& fs (i->second.create); @@ -78,9 +119,9 @@ namespace odb drop_schema (database& db, const string& name) { const schema_catalog_impl& c (*schema_catalog_init::catalog); - schema_catalog_impl::const_iterator i (c.find (key (db.id (), name))); + schema_map::const_iterator i (c.schema.find (key (db.id (), name))); - if (i == c.end ()) + if (i == c.schema.end ()) throw unknown_schema (name); const create_functions& fs (i->second.create); @@ -111,9 +152,9 @@ namespace odb migrate_mode m) { const schema_catalog_impl& c (*schema_catalog_init::catalog); - schema_catalog_impl::const_iterator i (c.find (key (db.id (), name))); + schema_map::const_iterator i (c.schema.find (key (db.id (), name))); - if (i == c.end ()) + if (i == c.schema.end ()) throw unknown_schema (name); const version_map& vm (i->second.migrate); @@ -154,7 +195,54 @@ namespace odb } void schema_catalog:: - migrate (database& db, schema_version v, const std::string& name) + migrate_data (database& db, schema_version v, const string& name) + { + if (v == 0) + { + if (!db.schema_migration ()) + return; + + v = db.schema_version (); + } + + const schema_catalog_impl& c (*schema_catalog_init::catalog); + data_map::const_iterator i (c.data.find (data_key (name, v))); + + if (i == c.data.end ()) + return; // No data migration for this schema/version. + + const data_functions& df (i->second); + + for (data_functions::const_iterator i (df.begin ()), e (df.end ()); + i != e; ++i) + { + if (i->id == id_common || i->id == db.id ()) + i->migrate (db); + } + } + + void schema_catalog:: + data_migration_function (database_id id, + schema_version v, + data_migration_function_type f, + const string& name) + { + // This function can be called from a static initializer in which + // case the catalog might not have yet been created. + // + if (schema_catalog_init::count == 0) + { + schema_catalog_init::catalog = new schema_catalog_impl; + ++schema_catalog_init::count; + schema_catalog_init_extra_.initialized = true; + } + + schema_catalog_impl& c (*schema_catalog_init::catalog); + c.data[data_key (name, v)].push_back (data_function (id, f)); + } + + void schema_catalog:: + migrate (database& db, schema_version v, const string& name) { schema_version latest (latest_version (db, name)); @@ -168,7 +256,7 @@ namespace odb i = next_version (db, i, name)) { migrate_schema_pre (db, i, name); - // migrate_data (db, i, name); + migrate_data (db, i, name); migrate_schema_post (db, i, name); } } @@ -177,9 +265,9 @@ namespace odb 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))); + schema_map::const_iterator i (c.schema.find (key (id, name))); - if (i == c.end ()) + if (i == c.schema.end ()) throw unknown_schema (name); const version_map& vm (i->second.migrate); @@ -191,9 +279,9 @@ namespace odb 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))); + schema_map::const_iterator i (c.schema.find (key (id, name))); - if (i == c.end ()) + if (i == c.schema.end ()) throw unknown_schema (name); const version_map& vm (i->second.migrate); @@ -227,7 +315,7 @@ namespace odb create_function cf) { schema_catalog_impl& c (*schema_catalog_init::catalog); - c[key(id, name)].create.push_back (cf); + c.schema[key(id, name)].create.push_back (cf); } // schema_catalog_migrate_entry @@ -239,6 +327,6 @@ namespace odb migrate_function mf) { schema_catalog_impl& c (*schema_catalog_init::catalog); - c[key(id, name)].migrate[v].push_back (mf); + c.schema[key(id, name)].migrate[v].push_back (mf); } } diff --git a/odb/schema-catalog.hxx b/odb/schema-catalog.hxx index 07e625b..5af7c48 100644 --- a/odb/schema-catalog.hxx +++ b/odb/schema-catalog.hxx @@ -7,8 +7,14 @@ #include +#include // ODB_CXX11 + #include +#ifdef ODB_CXX11 +# include // std::function +#endif + #include // schema_version, odb::core #include @@ -28,6 +34,7 @@ namespace odb // Schema migration. // + public: static void migrate_schema_pre (database& db, schema_version v, @@ -52,12 +59,62 @@ namespace odb migrate_schema_impl (db, v, name, migrate_both); } + // Data migration. + // + public: + // If version is 0, then use the current version and also check whether + // we are in migration. + // + static void + migrate_data (database&, + schema_version = 0, + const std::string& name = ""); + +#ifdef ODB_CXX11 + typedef std::function data_migration_function_type; +#else + typedef void (*data_migration_function_type) (database&); +#endif + + // Data migration functions are called in the order of registration. + // + static void + data_migration_function (schema_version v, + data_migration_function_type f, + const std::string& name = "") + { + data_migration_function (id_common, v, f, name); + } + + // Database-specific data migration. + // + static void + data_migration_function (database& db, + schema_version v, + data_migration_function_type f, + const std::string& name = "") + { + data_migration_function (db.id (), v, f, name); + } + + static void + data_migration_function (database_id, + schema_version, + data_migration_function_type, + const std::string& name = ""); + + // Combined schema and data migration. + // + public: // Migrate both schema and data to the specified version. If version // is not specified, then migrate to the latest version. // static void - migrate (database& db, schema_version v = 0, const std::string& name = ""); + migrate (database&, schema_version = 0, const std::string& name = ""); + // Schema version information. + // + public: // Return 0 if current is greater or equal to the latest version. // If current is not specified, get the current version from the // database. @@ -86,6 +143,9 @@ namespace odb static schema_version latest_version (database_id, const std::string& name = ""); + // Schema existence. + // + public: // Test for presence of a schema with a specific name. // static bool @@ -112,9 +172,32 @@ namespace odb migrate_mode); }; + // Static data migration function registration. + // + struct LIBODB_EXPORT data_migration_entry + { + typedef schema_catalog::data_migration_function_type function_type; + + data_migration_entry (schema_version v, + function_type f, + const std::string& name = "") + { + schema_catalog::data_migration_function (v, f, name); + } + + data_migration_entry (database_id id, + schema_version v, + function_type f, + const std::string& name = "") + { + schema_catalog::data_migration_function (id, v, f, name); + } + }; + namespace common { using odb::schema_catalog; + using odb::data_migration_entry; } } -- cgit v1.1