aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2013-04-09 16:17:26 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2013-04-09 16:17:26 +0200
commit287a41287ed26bcf481f087680cad56ed5ef9865 (patch)
treea139f2da973f2e6576d2673e9e096a4e4ab12e6f
parent4962329eecb716bc2b99810ec1ac4214606fc1e8 (diff)
Add support for embedded schema migration
-rw-r--r--odb/exceptions.cxx22
-rw-r--r--odb/exceptions.hxx22
-rw-r--r--odb/forward.hxx2
-rw-r--r--odb/schema-catalog-impl.hxx17
-rw-r--r--odb/schema-catalog.cxx116
-rw-r--r--odb/schema-catalog.hxx74
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