// file : odb/relational/schema.hxx // copyright : Copyright (c) 2009-2015 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #ifndef ODB_RELATIONAL_SCHEMA_HXX #define ODB_RELATIONAL_SCHEMA_HXX #include #include #include #include #include #include namespace relational { namespace schema { typedef std::set table_set; struct common: virtual context { typedef ::emitter emitter_type; common (emitter_type& e, ostream& os, schema_format f) : e_ (e), os_ (os), format_ (f) {} void pre_statement () { e_.pre (); diverge (os_); } void post_statement () { restore (); e_.post (); } emitter_type& emitter () const { return e_; } ostream& stream () const { return os_; } public: // Find an entity corresponding to the drop node in alter_table. // template T& find (D& d) { using sema_rel::model; using sema_rel::changeset; using sema_rel::table; using sema_rel::alter_table; alter_table& at (dynamic_cast (d.scope ())); changeset& cs (dynamic_cast (at.scope ())); model& bm (cs.base_model ()); table* bt (bm.find (at.name ())); assert (bt != 0); T* b (bt->find (d.name ())); assert (b != 0); return *b; } protected: emitter_type& e_; ostream& os_; schema_format format_; }; // // Drop. // // Only used in migration. // struct drop_column: trav_rel::drop_column, common { typedef drop_column base; drop_column (common const& c, bool* first = 0) : common (c), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } drop_column (drop_column const& c) : root_context (), // @@ -Wextra context (), common (c), first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_), first_data_ (c.first_data_) { } virtual void drop_header () { // By default ADD COLUMN though some databases use just ADD. // os << "DROP COLUMN "; } virtual void traverse (sema_rel::drop_column& dc) { if (first_) first_ = false; else os << ","; os << endl << " "; drop_header (); os << quote_id (dc.name ()); } protected: bool& first_; bool first_data_; }; // Normally only used in migration but some databases use it as a // base to also drop foreign keys in schema. // struct drop_foreign_key: trav_rel::foreign_key, trav_rel::drop_foreign_key, trav_rel::add_foreign_key, // Override. common { typedef drop_foreign_key base; // Schema constructor. // drop_foreign_key (common const& c, table_set& dropped, bool* first = 0) : common (c), dropped_ (&dropped), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } // Migration constructor. // drop_foreign_key (common const& c, bool* first = 0) : common (c), dropped_ (0), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } drop_foreign_key (drop_foreign_key const& c) : root_context (), // @@ -Wextra context (), common (c), dropped_ (c.dropped_), first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_), first_data_ (c.first_data_) { } virtual void drop_header () { os << "DROP CONSTRAINT "; } virtual void traverse (sema_rel::foreign_key& fk) { // If the table which we reference is droped before us, then // we need to drop the constraint first. Similarly, if the // referenced table is not part if this model, then assume // it is dropped before us. In migration we always do this // first. // sema_rel::table& t (dynamic_cast (fk.scope ())); if (dropped_ != 0) { sema_rel::qname const& rt (fk.referenced_table ()); sema_rel::model& m (dynamic_cast (t.scope ())); if (dropped_->find (rt) == dropped_->end () && m.find (rt) != m.names_end ()) return; } drop (t, fk); } virtual void drop (sema_rel::table& t, sema_rel::foreign_key& fk) { // When generating schema we would need to check if the key exists. // So this implementation will need to be customize on the per- // database level. // pre_statement (); os << "ALTER TABLE " << quote_id (t.name ()) << endl << " "; drop_header (); os << quote_id (fk.name ()) << endl; post_statement (); } virtual void traverse (sema_rel::drop_foreign_key& dfk) { if (first_) first_ = false; else os << ","; os << endl; drop (dfk); } virtual void drop (sema_rel::drop_foreign_key& dfk) { os << " "; drop_header (); os << quote_id (dfk.name ()); } protected: table_set* dropped_; bool& first_; bool first_data_; }; // Currently only used in migration. // struct drop_index: trav_rel::drop_index, common { typedef drop_index base; enum index_type {unique, non_unique, all}; drop_index (common const& c, index_type t = all) : common (c), type_ (t) {} virtual void traverse (sema_rel::drop_index& di) { // Find the index we are dropping in the base model. // traverse (find (di)); } virtual void traverse (sema_rel::index& in) { if (type_ == unique && in.type ().find ("UNIQUE") == string::npos && in.type ().find ("unique") == string::npos) return; if (type_ == non_unique && ( in.type ().find ("UNIQUE") != string::npos || in.type ().find ("unique") != string::npos)) return; pre_statement (); drop (in); post_statement (); } virtual string name (sema_rel::index& in) { return quote_id (in.name ()); } virtual void drop (sema_rel::index& in) { os << "DROP INDEX " << name (in) << endl; } protected: index_type type_; }; struct drop_table: trav_rel::table, trav_rel::drop_table, trav_rel::add_table, // Override. trav_rel::alter_table, // Override. common { typedef drop_table base; drop_table (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} virtual void drop (sema_rel::table& t, bool migration) { pre_statement (); os << "DROP TABLE " << (migration ? "" : "IF EXISTS ") << quote_id (t.name ()) << endl; post_statement (); } virtual void delete_ (sema_rel::qname const& rtable, sema_rel::qname const& dtable, sema_rel::primary_key& rkey, sema_rel::primary_key& dkey) { pre_statement (); // This might not be the most efficient way for every database. // os << "DELETE FROM " << quote_id (rtable) << endl << " WHERE EXISTS (SELECT 1 FROM " << quote_id (dtable) << endl << " WHERE "; for (size_t i (0); i != rkey.contains_size (); ++i) { if (i != 0) os << endl << " AND "; os << quote_id (rtable) << "." << quote_id (rkey.contains_at (i).column ().name ()) << " = " << quote_id (dtable) << "." << quote_id (dkey.contains_at (i).column ().name ()); } os << ")" << endl; post_statement (); } virtual void traverse (sema_rel::table& t, bool migration) { // By default drop foreign keys referencing tables that would // have already been dropped on the first pass. // if (pass_ == 1) { // Drop constraints. In migration this is always done on pass 1. // if (migration) { instance dfk (*this); trav_rel::unames n (*dfk); names (t, n); } else { dropped_.insert (t.name ()); // Add it before to cover self-refs. instance dfk (*this, dropped_); trav_rel::unames n (*dfk); names (t, n); } } else { if (migration && t.extra ()["kind"] == "polymorphic derived object") { // If we are dropping a polymorphic derived object, then we // also have to clean the base tables. Note that this won't // trigger cascade deletion since we have dropped all the // keys on pass 1. But we still need to do this in the base // to root order in order not to trigger other cascades. // using sema_rel::model; using sema_rel::table; using sema_rel::primary_key; using sema_rel::foreign_key; model& m (dynamic_cast (t.scope ())); table* p (&t); do { // The polymorphic link is the first primary key. // for (table::names_iterator i (p->names_begin ()); i != p->names_end (); ++i) { if (foreign_key* fk = dynamic_cast ( &i->nameable ())) { p = m.find
(fk->referenced_table ()); assert (p != 0); // Base table should be there. break; } } primary_key& rkey (*p->find ("")); primary_key& dkey (*t.find ("")); assert (rkey.contains_size () == dkey.contains_size ()); delete_ (p->name (), t.name (), rkey, dkey); } while (p->extra ()["kind"] != "polymorphic root object"); } drop (t, migration); } } virtual void traverse (sema_rel::table& t) { traverse (t, false); } virtual void traverse (sema_rel::drop_table& dt) { using sema_rel::model; using sema_rel::changeset; using sema_rel::table; // Find the table we are dropping in the base model. // changeset& cs (dynamic_cast (dt.scope ())); model& bm (cs.base_model ()); table* t (bm.find
(dt.name ())); assert (t != 0); traverse (*t, true); } using table::names; void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; table_set dropped_; }; struct drop_model: trav_rel::model, common { typedef drop_model base; drop_model (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) { } virtual void traverse (sema_rel::model& m) { // Traverse named entities in the reverse order. This way we // drop them in the order opposite to creating. // for (sema_rel::model::names_iterator begin (m.names_begin ()), end (m.names_end ()); begin != end;) dispatch (*--end); } void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; }; // // Create. // struct create_column: trav_rel::column, trav_rel::add_column, trav_rel::alter_column, // Override. common { typedef create_column base; create_column (common const& c, bool override_null = true, bool* first = 0) : common (c), override_null_ (override_null), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } create_column (create_column const& c) : root_context (), // @@ -Wextra context (), common (c), override_null_ (c.override_null_), first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_), first_data_ (c.first_data_) { } virtual void traverse (sema_rel::column& c) { if (first_) first_ = false; else os << ","; os << endl << " "; create (c); } virtual void add_header () { // By default ADD COLUMN though some databases use just ADD. // os << "ADD COLUMN "; } virtual void traverse (sema_rel::add_column& ac) { if (first_) first_ = false; else os << ","; os << endl << " "; add_header (); create (ac); } virtual void create (sema_rel::column& c) { using sema_rel::column; // See if this column is (part of) a primary key. // sema_rel::primary_key* pk (0); for (column::contained_iterator i (c.contained_begin ()); i != c.contained_end (); ++i) { if ((pk = dynamic_cast (&i->key ()))) break; } os << quote_id (c.name ()) << " "; type (c, pk != 0 && pk->auto_ ()); constraints (c, pk); if (!c.options ().empty ()) os << " " << c.options (); } virtual void constraints (sema_rel::column& c, sema_rel::primary_key* pk) { null (c); if (!c.default_ ().empty ()) os << " DEFAULT " << c.default_ (); // If this is a single-column primary key, generate it inline. // if (pk != 0 && pk->contains_size () == 1) primary_key (); if (pk != 0 && pk->auto_ ()) auto_ (*pk); } virtual void type (sema_rel::column& c, bool /*auto*/) { os << c.type (); } virtual void null (sema_rel::column& c) { bool n (c.null ()); // If we are adding a new column that doesn't allow NULL nor has // a default value, add it as NULL. Later, after migration, we // will convert it to NOT NULL. // if (override_null_ && c.is_a () && !n && c.default_ ().empty ()) n = true; // Specify both cases explicitly for better readability, // especially in ALTER COLUMN clauses. // os << (n ? " NULL" : " NOT NULL"); } virtual void primary_key () { os << " PRIMARY KEY"; } virtual void auto_ (sema_rel::primary_key&) { } protected: bool override_null_; // Override NOT NULL in add_column. bool& first_; bool first_data_; bool add_; }; struct create_primary_key: trav_rel::primary_key, common { typedef create_primary_key base; create_primary_key (common const& c): common (c) {} virtual void traverse (sema_rel::primary_key& pk) { // Single-column primary keys are generated inline in the // column declaration. // if (pk.contains_size () == 1) return; // We will always follow a column. // os << "," << endl; create (pk); } virtual void create (sema_rel::primary_key& pk) { using sema_rel::primary_key; // By default we create unnamed primary key constraint. // os << " PRIMARY KEY ("; for (primary_key::contains_iterator i (pk.contains_begin ()); i != pk.contains_end (); ++i) { if (i != pk.contains_begin ()) os << "," << endl << " "; os << quote_id (i->column ().name ()); } os << ")"; } }; struct create_foreign_key: trav_rel::foreign_key, trav_rel::add_foreign_key, common { typedef create_foreign_key base; // Schema constructor, pass 1. // create_foreign_key (common const& c, table_set& created, bool* first = 0) : common (c), created_ (&created), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } // Schema constructor, pass 2 and migration constructor. // create_foreign_key (common const& c, bool* first = 0) : common (c), created_ (0), first_ (first != 0 ? *first : first_data_), first_data_ (true) { } create_foreign_key (create_foreign_key const& c) : root_context (), // @@ -Wextra context (), common (c), created_ (c.created_), first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_), first_data_ (c.first_data_) { } virtual void traverse (sema_rel::foreign_key& fk) { if (created_ != 0) { // Pass 1. // // If the referenced table has already been defined, do the // foreign key definition in the table definition. Otherwise // postpone it until pass 2 where we do it via ALTER TABLE. // if (created_->find (fk.referenced_table ()) != created_->end ()) { traverse_create (fk); fk.set (db.string () + "-fk-defined", true); // Mark it as defined. } } else { // Pass 2. // if (!fk.count (db.string () + "-fk-defined")) traverse_add (fk); } } virtual void traverse_create (sema_rel::foreign_key& fk) { if (first_) first_ = false; else os << ","; os << endl << " CONSTRAINT "; create (fk); } virtual void traverse_add (sema_rel::foreign_key& fk) { if (first_) first_ = false; else os << ","; os << endl; add (fk); } virtual void traverse (sema_rel::add_foreign_key& afk) { traverse_add (afk); } virtual void add_header () { os << "ADD CONSTRAINT "; } virtual void add (sema_rel::foreign_key& fk) { os << " "; add_header (); create (fk); } virtual void create (sema_rel::foreign_key& fk) { using sema_rel::foreign_key; os << name (fk) << endl << " FOREIGN KEY ("; for (foreign_key::contains_iterator i (fk.contains_begin ()); i != fk.contains_end (); ++i) { if (i != fk.contains_begin ()) os << "," << endl << " "; os << quote_id (i->column ().name ()); } string tn (table_name (fk)); string tn_pad (tn.size (), ' '); os << ")" << endl << " REFERENCES " << tn << " ("; foreign_key::columns const& refs (fk.referenced_columns ()); for (foreign_key::columns::const_iterator i (refs.begin ()); i != refs.end (); ++i) { if (i != refs.begin ()) os << "," << endl << " " << tn_pad; os << quote_id (*i); } os << ")"; if (fk.on_delete () != foreign_key::no_action) on_delete (fk.on_delete ()); if (!fk.not_deferrable ()) deferrable (fk.deferrable ()); } virtual string name (sema_rel::foreign_key& fk) { return quote_id (fk.name ()); } virtual string table_name (sema_rel::foreign_key& fk) { return quote_id (fk.referenced_table ()); } virtual void on_delete (sema_rel::foreign_key::action_type a) { using sema_rel::foreign_key; switch (a) { case foreign_key::no_action: break; case foreign_key::cascade: { os << endl << " ON DELETE CASCADE"; break; } case foreign_key::set_null: { os << endl << " ON DELETE SET NULL"; break; } } } virtual void deferrable (sema_rel::deferrable d) { os << endl << " DEFERRABLE INITIALLY " << d; } protected: table_set* created_; bool& first_; bool first_data_; }; struct create_index: trav_rel::index, common { typedef create_index base; enum index_type {unique, non_unique, all}; create_index (common const& c, index_type t = all) : common (c), type_ (t) {} virtual void traverse (sema_rel::index& in) { if (type_ == unique && in.type ().find ("UNIQUE") == string::npos && in.type ().find ("unique") == string::npos) return; if (type_ == non_unique && ( in.type ().find ("UNIQUE") != string::npos || in.type ().find ("unique") != string::npos)) return; pre_statement (); create (in); post_statement (); } virtual string name (sema_rel::index& in) { return quote_id (in.name ()); } virtual string table_name (sema_rel::index& in) { return quote_id (static_cast (in.scope ()).name ()); } virtual void columns (sema_rel::index& in) { using sema_rel::index; for (index::contains_iterator i (in.contains_begin ()); i != in.contains_end (); ++i) { if (in.contains_size () > 1) { if (i != in.contains_begin ()) os << ","; os << endl << " "; } os << quote_id (i->column ().name ()); if (!i->options ().empty ()) os << ' ' << i->options (); } } virtual void create (sema_rel::index& in) { // Default implementation that ignores the method. // os << "CREATE "; if (!in.type ().empty ()) os << in.type () << ' '; os << "INDEX " << name (in) << endl << " ON " << table_name (in) << " ("; columns (in); os << ")" << endl; if (!in.options ().empty ()) os << ' ' << in.options () << endl; } protected: index_type type_; }; struct create_table: trav_rel::table, trav_rel::alter_table, // Override. common { typedef create_table base; using trav_rel::table::names; create_table (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} virtual void create_pre (sema_rel::qname const& table) { os << "CREATE TABLE " << quote_id (table) << " ("; } virtual void create_post (sema_rel::table& t) { os << ")" << endl; if (!t.options ().empty ()) os << " " << t.options () << endl; } virtual void create (sema_rel::table& t) { pre_statement (); create_pre (t.name ()); instance c (*this); instance pk (*this); // We will always follow a column, so set first to false. // bool f (false); // (Im)perfect forwarding. bool* pf (&f); // (Im)perfect forwarding. instance fk (*this, created_, pf); trav_rel::unames n; n >> c; n >> pk; n >> fk; names (t, n); create_post (t); post_statement (); // Create indexes. // { instance in (*this); trav_rel::unames n (*in); names (t, n); } } // See if there are any undefined foreign keys that we need to // add with ALTER TABLE. // bool check_undefined_fk (sema_rel::table& t) { for (sema_rel::table::names_iterator i (t.names_begin ()); i != t.names_end (); ++i) { if (i->nameable ().is_a () && !i->nameable ().count (db.string () + "-fk-defined")) return true; } return false; } virtual void traverse (sema_rel::table& t) { // By default add foreign keys referencing tables that haven't // yet been defined on the second pass. // if (pass_ == 1) { // In migration we always add foreign keys on pass 2. // if (!t.is_a ()) created_.insert (t.name ()); // Add it before to cover self-refs. create (t); } else { // Add undefined foreign keys. // if (check_undefined_fk (t)) { pre_statement (); os << "ALTER TABLE " << quote_id (t.name ()); instance cfk (*this); trav_rel::unames n (*cfk); names (t, n); os << endl; post_statement (); } } } void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; table_set created_; }; struct create_model: trav_rel::model, common { typedef create_model base; create_model (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; }; // // Alter. // struct alter_column: trav_rel::alter_column, trav_rel::add_column, common { typedef alter_column base; alter_column (common const& c, bool pre, bool* first = 0) : common (c), pre_ (pre), first_ (first != 0 ? *first : first_data_), first_data_ (true), fl_ (false), def_ (c, fl_) { } alter_column (alter_column const& c) : root_context (), // @@ -Wextra context (), common (c), pre_ (c.pre_), first_ (&c.first_ != &c.first_data_ ? c.first_ : first_data_), first_data_ (c.first_data_), fl_ (false), def_ (c, fl_) { } virtual void alter_header () { os << "ALTER COLUMN "; } virtual void alter (sema_rel::column& c) { // By default use the whole definition. // def_->create (c); } virtual void traverse (sema_rel::column& c) { // Relax (NULL) in pre and tighten (NOT NULL) in post. // if (pre_ != c.null ()) return; if (first_) first_ = false; else os << ","; os << endl << " "; alter_header (); alter (c); } virtual void traverse (sema_rel::alter_column& ac) { assert (ac.null_altered ()); traverse (static_cast (ac)); } virtual void traverse (sema_rel::add_column& ac) { // We initially add NOT NULL columns without default values as // NULL. Now, after the migration, we convert them to NOT NULL. // if (!ac.null () && ac.default_ ().empty ()) traverse (static_cast (ac)); } protected: bool pre_; bool& first_; bool first_data_; bool fl_; // (Im)perfect forwarding. instance def_; }; struct alter_table_common: trav_rel::alter_table, common { alter_table_common (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} template T* check (sema_rel::alter_table& at) { for (sema_rel::alter_table::names_iterator i (at.names_begin ()); i != at.names_end (); ++i) { if (T* x = dynamic_cast (&i->nameable ())) return x; } return 0; } sema_rel::column* check_alter_column_null (sema_rel::alter_table& at, bool v) { for (sema_rel::alter_table::names_iterator i (at.names_begin ()); i != at.names_end (); ++i) { using sema_rel::add_column; using sema_rel::alter_column; if (alter_column* ac = dynamic_cast (&i->nameable ())) { if (ac->null_altered () && ac->null () == v) return ac; } // If we are testing for NOT NULL, also look for new columns that // we initially add as NULL and later convert to NOT NULL. // if (!v) { if (add_column* ac = dynamic_cast (&i->nameable ())) { if (!ac->null () && ac->default_ ().empty ()) return ac; } } } return 0; } void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; }; struct alter_table_pre: alter_table_common { typedef alter_table_pre base; alter_table_pre (emitter_type& e, ostream& os, schema_format f) : alter_table_common (e, os, f) {} // Check if there will be any clauses in ALTER TABLE. // using alter_table_common::check; virtual bool check (sema_rel::alter_table& at) { // If changing the below test, make sure to also update tests // in database-specific code. // return check (at) || check (at) || check_alter_column_null (at, true); } virtual void alter (sema_rel::alter_table& at) { // By default we generate all the alterations in a single ALTER TABLE // statement. Quite a few databases don't support this. // pre_statement (); os << "ALTER TABLE " << quote_id (at.name ()); bool f (true); // Shared first flag. bool* pf (&f); // (Im)perfect forwarding. bool tl (true); // (Im)perfect forwarding. instance cc (*this, tl, pf); instance ac (*this, tl, pf); instance dfk (*this, pf); trav_rel::unames n; n >> cc; n >> ac; n >> dfk; names (at, n); os << endl; post_statement (); } virtual void traverse (sema_rel::alter_table& at) { if (pass_ == 1) { // Drop unique indexes. // { drop_index::index_type it (drop_index::unique); instance in (*this, it); trav_rel::unames n (*in); names (at, n); } if (check (at)) alter (at); } else { // Add non-unique indexes. // { create_index::index_type it (create_index::non_unique); instance in (*this, it); trav_rel::unames n (*in); names (at, n); } } } }; struct changeset_pre: trav_rel::changeset, common { typedef changeset_pre base; changeset_pre (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; }; struct alter_table_post: alter_table_common { typedef alter_table_post base; alter_table_post (emitter_type& e, ostream& os, schema_format f) : alter_table_common (e, os, f) {} // Check if there will be any clauses in ALTER TABLE. // using alter_table_common::check; virtual bool check (sema_rel::alter_table& at) { // If changing the below test, make sure to also update tests // in database-specific code. // return check (at) || check (at) || check_alter_column_null (at, false); } virtual void alter (sema_rel::alter_table& at) { // By default we generate all the alterations in a single ALTER TABLE // statement. Quite a few databases don't support this. // pre_statement (); os << "ALTER TABLE " << quote_id (at.name ()); bool f (true); // Shared first flag. bool* pf (&f); // (Im)perfect forwarding. bool fl (false); // (Im)perfect forwarding. instance dc (*this, pf); instance ac (*this, fl, pf); instance fk (*this, pf); trav_rel::unames n; n >> dc; n >> ac; n >> fk; names (at, n); os << endl; post_statement (); } virtual void traverse (sema_rel::alter_table& at) { if (pass_ == 1) { // Drop non-unique indexes. // { drop_index::index_type it (drop_index::non_unique); instance in (*this, it); trav_rel::unames n (*in); names (at, n); } } else { if (check (at)) alter (at); // Add unique indexes. // { create_index::index_type it (create_index::unique); instance in (*this, it); trav_rel::unames n (*in); names (at, n); } } } }; struct changeset_post: trav_rel::changeset, common { typedef changeset_post base; changeset_post (emitter_type& e, ostream& os, schema_format f) : common (e, os, f) {} virtual void traverse (sema_rel::changeset& m) { // Traverse named entities in the reverse order. This way we // drop them in the order opposite to creating. // for (sema_rel::changeset::names_iterator begin (m.names_begin ()), end (m.names_end ()); begin != end;) dispatch (*--end); } void pass (unsigned short p) { pass_ = p; } protected: unsigned short pass_; }; // // Schema version table. // struct version_table: common { typedef version_table base; version_table (emitter_type& e, ostream& os, schema_format f) : common (e, os, f), table_ (options.schema_version_table ()[db]), qt_ (quote_id (table_)), qs_ (quote_string (options.schema_name ()[db])), qn_ (quote_id ("name")), qv_ (quote_id ("version")), qm_ (quote_id ("migration")) { } // Create the version table if it doesn't exist. // virtual void create_table () {} // Remove the version entry. Called after the DROP statements. // virtual void drop () { pre_statement (); os << "DELETE FROM " << qt_ << endl << " WHERE " << qn_ << " = " << qs_ << endl; post_statement (); } // Set the version. Called after the CREATE statements. // virtual void create (sema_rel::version) {} // Set the version and migration state to true. Called after // the pre migration statements. // virtual void migrate_pre (sema_rel::version v) { pre_statement (); os << "UPDATE " << qt_ << endl << " SET " << qv_ << " = " << v << ", " << qm_ << " = 1" << endl << " WHERE " << qn_ << " = " << qs_ << endl; post_statement (); } // Set migration state to false. Called after the post migration // statements. // virtual void migrate_post () { pre_statement (); os << "UPDATE " << qt_ << endl << " SET " << qm_ << " = 0" << endl << " WHERE " << qn_ << " = " << qs_ << endl; post_statement (); } protected: sema_rel::qname table_; string qt_; // Quoted table. string qs_; // Quoted schema name string. string qn_; // Quoted name column. string qv_; // Quoted version column. string qm_; // Quoted migration column. }; // // SQL output. // struct sql_emitter: emitter, virtual context { typedef sql_emitter base; virtual void pre () { first_ = true; } virtual void line (const std::string& l) { if (first_ && !l.empty ()) first_ = false; else os << endl; os << l; } virtual void post () { if (!first_) // Ignore empty statements. os << ';' << endl << endl; } protected: bool first_; }; struct sql_file: virtual context { typedef sql_file base; virtual void prologue () { } virtual void epilogue () { } }; } } #endif // ODB_RELATIONAL_SCHEMA_HXX