From c9dbc099d74d92b17724a24823aafe1fcc8ca7e7 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 5 Apr 2013 07:30:19 +0200 Subject: Generate add/drop column migration statements --- odb/relational/mssql/schema.cxx | 84 ++++++++++++++++ odb/relational/oracle/schema.cxx | 92 ++++++++++++++++++ odb/relational/schema.hxx | 202 +++++++++++++++++++++++++++++++++------ odb/relational/sqlite/schema.cxx | 54 +++++++++++ 4 files changed, 402 insertions(+), 30 deletions(-) diff --git a/odb/relational/mssql/schema.cxx b/odb/relational/mssql/schema.cxx index 58c0206..03406ce 100644 --- a/odb/relational/mssql/schema.cxx +++ b/odb/relational/mssql/schema.cxx @@ -40,6 +40,24 @@ namespace relational // Drop. // + struct drop_column: relational::drop_column, context + { + drop_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::drop_column& dc) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + os << quote_id (dc.name ()); + } + }; + entry drop_column_; + struct drop_table: relational::drop_table, context { drop_table (base const& x): base (x) {} @@ -148,6 +166,18 @@ namespace relational create_column (base const& x): base (x) {} virtual void + traverse (sema_rel::add_column& ac) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + create (ac); + } + + virtual void auto_ (sema_rel::column&) { os << " IDENTITY"; @@ -305,6 +335,60 @@ namespace relational } }; entry drop_index_; + + struct alter_table_pre: relational::alter_table_pre, context + { + alter_table_pre (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // SQL Server can only alter one kind of thing at a time. + // + if (check (at)) + { + pre_statement (); + alter_header (at.name ()); + os << " ADD "; + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << endl; + + post_statement (); + } + } + }; + entry alter_table_pre_; + + struct alter_table_post: relational::alter_table_post, context + { + alter_table_post (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // SQL Server can only alter one kind of thing at a time. + // + if (check (at)) + { + pre_statement (); + alter_header (at.name ()); + os << " DROP COLUMN "; + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << endl; + + post_statement (); + } + } + }; + entry alter_table_post_; } } } diff --git a/odb/relational/oracle/schema.cxx b/odb/relational/oracle/schema.cxx index a76e25a..34ede1c 100644 --- a/odb/relational/oracle/schema.cxx +++ b/odb/relational/oracle/schema.cxx @@ -88,6 +88,24 @@ namespace relational // Drop. // + struct drop_column: relational::drop_column, context + { + drop_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::drop_column& dc) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + os << quote_id (dc.name ()); + } + }; + entry drop_column_; + struct drop_table: relational::drop_table, context { drop_table (base const& x): base (x) {} @@ -167,6 +185,24 @@ namespace relational // Create. // + struct create_column: relational::create_column, context + { + create_column (base const& x): base (x) {} + + virtual void + traverse (sema_rel::add_column& ac) + { + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + create (ac); + } + }; + entry create_column_; + struct create_foreign_key; struct create_table: relational::create_table, context @@ -307,6 +343,62 @@ namespace relational } }; entry drop_index_; + + struct alter_table_pre: relational::alter_table_pre, context + { + alter_table_pre (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // Oracle can only alter certain kinds of things together but + // grouped one at a time. + // + if (check (at)) + { + pre_statement (); + alter_header (at.name ()); + os << " ADD ("; + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << ")" << endl; + + post_statement (); + } + } + }; + entry alter_table_pre_; + + struct alter_table_post: relational::alter_table_post, context + { + alter_table_post (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // Oracle can only alter certain kinds of things together but + // grouped one at a time. + // + if (check (at)) + { + pre_statement (); + alter_header (at.name ()); + os << " DROP ("; + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << ")" << endl; + + post_statement (); + } + } + }; + entry alter_table_post_; } } } diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 5d3d91f..b0d70df 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -58,6 +58,43 @@ namespace relational // Drop. // + // Only used in migration. + // + struct drop_column: trav_rel::drop_column, common + { + typedef drop_column base; + + drop_column (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f), first_ (true) + { + } + + 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 << "," << endl; + + os << " "; + drop_header (); + os << quote_id (dc.name ()); + } + + protected: + schema_format format_; + bool first_; + }; + // Currently only used in migration. // struct drop_index: trav_rel::drop_index, common @@ -249,12 +286,14 @@ namespace relational // struct create_table; - struct create_column: trav_rel::column, virtual context + struct create_column: trav_rel::column, + trav_rel::add_column, + common { typedef create_column base; - create_column (schema_format f, create_table& ct) - : format_ (f), create_table_ (ct), first_ (true) + create_column (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (f), first_ (true) { } @@ -266,10 +305,32 @@ namespace relational else os << "," << endl; + os << " "; 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 << "," << endl; + + os << " "; + add_header (); + create (ac); + } + + virtual void create (sema_rel::column& c) { using sema_rel::column; @@ -286,7 +347,7 @@ namespace relational break; } - os << " " << quote_id (c.name ()) << " "; + os << quote_id (c.name ()) << " "; type (c, pk != 0 && pk->auto_ ()); @@ -333,7 +394,6 @@ namespace relational protected: schema_format format_; - create_table& create_table_; bool first_; }; @@ -641,7 +701,7 @@ namespace relational pre_statement (); create_pre (t.name ()); - instance c (format_, *this); + instance c (emitter (), stream (), format_); instance pk (format_, *this); instance fk (format_, *this); trav_rel::unames n; @@ -711,15 +771,83 @@ namespace relational unsigned short pass_; }; + // // Migration. // - struct alter_table_pre: trav_rel::alter_table, common + + struct alter_table_common: trav_rel::alter_table, common + { + alter_table_common (emitter_type& e, ostream& os, schema_format f) + : common (e, os), format_ (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; + } + + virtual void + alter_header (sema_rel::qname const& table) + { + os << "ALTER TABLE " << quote_id (table) << endl; + } + + void + pass (unsigned short p) + { + pass_ = p; + } + + protected: + schema_format format_; + 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) - : common (e, os), 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) { + return check (at); + } + + 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 (); + alter_header (at.name ()); + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << endl; + + post_statement (); } virtual void @@ -735,6 +863,9 @@ namespace relational trav_rel::unames n (*in); names (at, n); } + + if (check (at)) + alter (at); } else { @@ -748,16 +879,6 @@ namespace relational } } } - - void - pass (unsigned short p) - { - pass_ = p; - } - - protected: - schema_format format_; - unsigned short pass_; }; struct changeset_pre: trav_rel::changeset, common @@ -796,13 +917,41 @@ namespace relational unsigned short pass_; }; - struct alter_table_post: trav_rel::alter_table, common + struct alter_table_post: alter_table_common { typedef alter_table_post base; alter_table_post (emitter_type& e, ostream& os, schema_format f) - : common (e, os), 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) { + return check (at); + } + + 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 (); + alter_header (at.name ()); + + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + os << endl; + + post_statement (); } virtual void @@ -821,6 +970,9 @@ namespace relational } else { + if (check (at)) + alter (at); + // Add unique indexes. // { @@ -831,16 +983,6 @@ namespace relational } } } - - void - pass (unsigned short p) - { - pass_ = p; - } - - protected: - schema_format format_; - unsigned short pass_; }; struct changeset_post: trav_rel::changeset, common diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx index bd694e7..ac19328 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/relational/sqlite/schema.cxx @@ -24,6 +24,22 @@ namespace relational create_column (base const& x): base (x) {} virtual void + traverse (sema_rel::add_column& ac) + { + using sema_rel::alter_table; + alter_table& at (static_cast (ac.scope ())); + + pre_statement (); + + os << "ALTER TABLE " << quote_id (at.name ()) << endl + << " ADD COLUMN "; + create (ac); + os << endl; + + post_statement (); + } + + virtual void auto_ (sema_rel::column&) { if (options.sqlite_lax_auto_id ()) @@ -92,6 +108,44 @@ namespace relational } }; entry drop_index_; + + struct alter_table_pre: relational::alter_table_pre, context + { + alter_table_pre (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // SQLite can only add a single column per ALTER TABLE statement. + // + instance c (emitter (), stream (), format_); + trav_rel::unames n; + n >> c; + names (at, n); + } + }; + entry alter_table_pre_; + + struct alter_table_post: relational::alter_table_post, context + { + alter_table_post (base const& x): base (x) {} + + virtual void + alter (sema_rel::alter_table& at) + { + // SQLite does not support dropping columns. + // + if (sema_rel::drop_column* dc = check (at)) + { + cerr << "error: SQLite does not support dropping of columns" + << endl; + cerr << "info: first dropped column is '" << dc->name () << + "' in table '" << at.name () << "'" << endl; + throw operation_failed (); + } + } + }; + entry alter_table_post_; } } } -- cgit v1.1