From 5a5bf7fc225ac225dbc03df55f6be7c56cb419aa Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 16 Sep 2013 07:07:33 +0200 Subject: Implement logical column drop for SQLite --- odb/relational/sqlite/schema.cxx | 107 ++++++++++++++++++++++++++++++++---- odb/semantics/relational/column.hxx | 7 +++ 2 files changed, 102 insertions(+), 12 deletions(-) diff --git a/odb/relational/sqlite/schema.cxx b/odb/relational/sqlite/schema.cxx index ac9a753..96eaf68 100644 --- a/odb/relational/sqlite/schema.cxx +++ b/odb/relational/sqlite/schema.cxx @@ -19,6 +19,45 @@ namespace relational // Drop. // + struct drop_column: trav_rel::drop_column, relational::common + { + drop_column (relational::common const& c) + : relational::common (c), first_ (true) {} + + virtual void + traverse (sema_rel::drop_column& dc) + { + // SQLite does not support dropping columns. If this column is + // not NULLable, then there is nothing we can do. Otherwise, do + // a logical DROP by setting all the values to NULL. + // + sema_rel::column& c (find (dc)); + + if (!c.null ()) + { + cerr << "error: SQLite does not support dropping of columns" << + endl; + cerr << "info: first dropped column is '" << dc.name () << + "' in table '" << dc.table ().name () << "'" << endl; + cerr << "info: could have perform logical drop if the column " << + "allowed NULL values" << endl; + throw operation_failed (); + } + + if (first_) + first_ = false; + else + os << "," << endl + << " "; + + os << quote_id (dc.name ()) << " = NULL"; + } + + private: + bool first_; + }; + // Not registered as an override. + struct drop_index: relational::drop_index, context { drop_index (base const& x): base (x) {} @@ -263,16 +302,41 @@ namespace relational throw operation_failed (); } - // SQLite does not support dropping constraints. + // SQLite does not support dropping constraints. We are going to + // ignore this if the column is NULL'able since in most cases + // the constraint is going to be dropped as a result of the + // column drop (e.g., an object pointer member got deleted). + // If we were not to allow this, then it would be impossible + // to do logical drop for pointer columns. // - if (sema_rel::drop_foreign_key* dfk = - check (at)) + for (sema_rel::alter_table::names_iterator i (at.names_begin ()); + i != at.names_end (); ++i) { - cerr << "error: SQLite does not support dropping of foreign keys" - << endl; - cerr << "info: first dropped foreign key is '" << dfk->name () << - "' in table '" << at.name () << "'" << endl; - throw operation_failed (); + using sema_rel::foreign_key; + using sema_rel::drop_foreign_key; + + drop_foreign_key* dfk ( + dynamic_cast (&i->nameable ())); + + if (dfk == 0) + continue; + + foreign_key& fk (find (*dfk)); + + for (foreign_key::contains_iterator j (fk.contains_begin ()); + j != fk.contains_end (); ++j) + { + if (j->column ().null ()) + continue; + + cerr << "error: SQLite does not support dropping of foreign " << + "keys" << endl; + cerr << "info: first dropped foreign key is '" << dfk->name () << + "' in table '" << at.name () << "'" << endl; + cerr << "info: could have ignored it if the contained " << + "column(s) allowed NULL values" << endl; + throw operation_failed (); + } } } }; @@ -285,17 +349,36 @@ namespace relational virtual void alter (sema_rel::alter_table& at) { - // SQLite does not support dropping columns. + // SQLite does not support altering columns (we have to do this + // in both alter_table_pre/post because of the + // check_alter_column_null() test in the common code). // - if (sema_rel::drop_column* dc = check (at)) + if (sema_rel::alter_column* ac = check (at)) { - cerr << "error: SQLite does not support dropping of columns" + cerr << "error: SQLite does not support altering of columns" << endl; - cerr << "info: first dropped column is '" << dc->name () << + cerr << "info: first altered column is '" << ac->name () << "' in table '" << at.name () << "'" << endl; throw operation_failed (); } + // Try to do logical column drop. + // + if (check (at)) + { + pre_statement (); + + os << "UPDATE " << quote_id (at.name ()) << endl + << " SET "; + + drop_column dc (*this); + trav_rel::unames n (dc); + names (at, n); + os << endl; + + post_statement (); + } + // SQLite doesn't support adding foreign keys other than inline // via a column definition. See if there are any that we couldn't // handle that way. diff --git a/odb/semantics/relational/column.hxx b/odb/semantics/relational/column.hxx index 11a76e0..8c6436e 100644 --- a/odb/semantics/relational/column.hxx +++ b/odb/semantics/relational/column.hxx @@ -128,6 +128,13 @@ namespace semantics : unameable (c, g) {} drop_column (xml::parser&, uscope&, graph&); + public: + typedef relational::table table_type; + + table_type& + table () const {return dynamic_cast (scope ());} + + public: virtual drop_column& clone (uscope&, graph&) const; -- cgit v1.1