summaryrefslogtreecommitdiff
path: root/odb/relational/schema.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/relational/schema.hxx')
-rw-r--r--odb/relational/schema.hxx708
1 files changed, 376 insertions, 332 deletions
diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx
index ecafe2c..855f67f 100644
--- a/odb/relational/schema.hxx
+++ b/odb/relational/schema.hxx
@@ -11,7 +11,6 @@
#include <cassert>
#include <odb/emitter.hxx>
-
#include <odb/relational/common.hxx>
#include <odb/relational/context.hxx>
@@ -19,11 +18,11 @@ namespace relational
{
namespace schema
{
- typedef std::set<std::string> tables;
-
struct common: virtual context
{
- common (emitter& e, ostream& os): e_ (e), os_ (os) {}
+ typedef ::emitter emitter_type;
+
+ common (emitter_type& e, ostream& os): e_ (e), os_ (os) {}
void
pre_statement ()
@@ -39,8 +38,20 @@ namespace relational
e_.post ();
}
+ emitter_type&
+ emitter () const
+ {
+ return e_;
+ }
+
+ ostream&
+ stream () const
+ {
+ return os_;
+ }
+
protected:
- emitter& e_;
+ emitter_type& e_;
ostream& os_;
};
@@ -57,7 +68,7 @@ namespace relational
virtual void
line (const std::string& l)
{
- if (first_)
+ if (first_ && !l.empty ())
first_ = false;
else
os << endl;
@@ -78,7 +89,7 @@ namespace relational
};
//
- // File.
+ // File prologue/epilogue.
//
struct schema_file: virtual context
@@ -86,12 +97,12 @@ namespace relational
typedef schema_file base;
virtual void
- pre ()
+ prologue ()
{
}
virtual void
- post ()
+ epilogue ()
{
}
};
@@ -100,474 +111,467 @@ namespace relational
// Drop.
//
- struct drop_common: virtual context
+ struct drop_table: trav_rel::table, common
{
+ typedef drop_table base;
+
+ drop_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
+ {
+ }
+
virtual void
- drop_table (string const& table)
+ drop (string const& table)
{
os << "DROP TABLE IF EXISTS " << quote_id (table) << endl;
}
virtual void
- drop_index (string const& /*table*/, string const& /*column*/)
+ traverse (sema_rel::table& t)
{
- // Most database systems drop indexes together with the table.
+ // By default we do everything in a single pass. But some
+ // databases may require the second pass.
//
+ if (pass_ > 1)
+ return;
- //os << "DROP INDEX IF EXISTS " << quote_id (table + '_' + column)
- // << endl;
+ pre_statement ();
+ drop (t.name ());
+ post_statement ();
}
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
+
+ protected:
+ schema_format format_;
+ unsigned short pass_;
};
- struct member_drop: object_members_base, common, virtual drop_common
+ struct drop_index: trav_rel::index, common
{
- typedef member_drop base;
+ typedef drop_index base;
- member_drop (emitter& e, ostream& os, std::vector<tables>& t)
- : object_members_base (false, true, false),
- common (e, os),
- tables_ (t)
+ drop_index (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
{
}
- void
- pass (unsigned short p)
+ virtual void
+ drop (string const& /*index*/)
{
- pass_ = p;
+ // Most database systems drop indexes together with the table.
+ //
+ //os << "DROP INDEX IF EXISTS " << quote_id (index);
}
virtual void
- traverse_container (semantics::data_member& m, semantics::type& c)
+ traverse (sema_rel::index& in)
{
- // Ignore inverse containers of object pointers.
+ // By default we do everything in a single pass. But some
+ // databases may require the second pass.
//
- if (inverse (m, "value"))
- return;
-
- string const& name (table_name (m, table_prefix_));
-
- if (tables_[pass_].count (name))
+ if (pass_ > 1)
return;
- // Drop table.
- //
- pre_statement ();
- drop_table (name);
- post_statement ();
-
- tables_[pass_].insert (name);
-
- // Drop indexes.
- //
pre_statement ();
- drop_index (name, column_name (m, "id", "object_id"));
+ drop (in.name ());
post_statement ();
+ }
- if (container_kind (c) == ck_ordered && !unordered (m))
- {
- pre_statement ();
- drop_index (name, column_name (m, "index", "index"));
- post_statement ();
- }
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
}
protected:
- std::vector<tables>& tables_;
+ schema_format format_;
unsigned short pass_;
};
- struct class_drop: traversal::class_, common, virtual drop_common
+ struct drop_model: trav_rel::model, common
{
- typedef class_drop base;
+ typedef drop_model base;
- class_drop (emitter& e)
- : common (e, os_), os_ (e), member_drop_ (e, os_, tables_)
+ drop_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
{
- tables_.push_back (tables ()); // Dummy entry.
- }
-
- class_drop (class_drop const& x)
- : root_context (), //@@ -Wextra
- context (),
- common (x.e_, os_), os_ (x.e_), member_drop_ (x.e_, os_, tables_)
- {
- tables_.push_back (tables ()); // Dummy entry.
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
-
- if (tables_.size () == pass_)
- tables_.push_back (tables ());
-
- member_drop_->pass (p);
}
+ // This version is only called for file schema.
+ //
virtual void
- traverse (type& c)
+ traverse (sema_rel::model& m)
{
- // By default we do everything in a single pass. But some
- // databases may require the second pass.
- //
- if (pass_ == 1)
- drop (c);
+ traverse (m.names_begin (), m.names_end ());
}
virtual void
- drop (type& c)
+ traverse (sema_rel::model::names_iterator begin,
+ sema_rel::model::names_iterator end)
{
- if (c.file () != unit.file ())
- return;
-
- if (!object (c) || abstract (c))
- return;
-
- string const& name (table_name (c));
-
- if (tables_[pass_].count (name))
- return;
-
- // Drop tables for members. Do it before dropping the primary
- // table -- some databases may prefer it that way.
+ // Traverse named entities in the reverse order. This way we
+ // drop them in the order opposite to creating.
//
- member_drop_->traverse (c);
+ if (begin != end)
+ {
+ for (--end;; --end)
+ {
+ dispatch (*end);
- pre_statement ();
- drop_table (name);
- post_statement ();
+ if (begin == end)
+ break;
+ }
+ }
+ }
- tables_[pass_].insert (name);
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
}
protected:
- emitter_ostream os_;
+ schema_format format_;
unsigned short pass_;
- std::vector<tables> tables_; // Seperate table for each pass.
- instance<member_drop> member_drop_;
};
//
// Create.
//
+ struct create_table;
- struct object_columns: object_columns_base, virtual context
+ struct create_column: trav_rel::column, virtual context
{
- typedef object_columns base;
+ typedef create_column base;
- object_columns (string const& prefix = string ())
- : prefix_ (prefix)
+ create_column (schema_format f, create_table& ct)
+ : format_ (f), create_table_ (ct), first_ (true)
{
}
- virtual bool
- traverse_column (semantics::data_member& m,
- string const& name,
- bool first)
+ virtual void
+ traverse (sema_rel::column& c)
{
- // Ignore inverse object pointers.
- //
- if (inverse (m))
- return false;
-
- if (!first)
+ if (first_)
+ first_ = false;
+ else
os << "," << endl;
- os << " " << quote_id (name) << " ";
+ create (c);
+ }
- type (m);
- null (m);
+ virtual void
+ create (sema_rel::column& c)
+ {
+ using sema_rel::column;
- // An id member cannot have a default value.
+ // See if this column is (part of) a primary key.
//
- if (!m.count ("id"))
- default_ (m);
+ sema_rel::primary_key* pk (0);
+
+ for (column::contained_iterator i (c.contained_begin ());
+ i != c.contained_end ();
+ ++i)
+ {
+ if ((pk = dynamic_cast<sema_rel::primary_key*> (&i->key ())))
+ break;
+ }
+
+ os << " " << quote_id (c.name ()) << " ";
- // If we have options, add them.
+ type (c, pk != 0 && pk->auto_ ());
+ null (c);
+
+ // If this is a single-column primary key, generate it inline.
//
- string const& o (column_options (m, prefix_));
+ if (pk != 0 && pk->contains_size () == 1)
+ primary_key ();
- if (!o.empty ())
- os << " " << o;
+ if (pk != 0 && pk->auto_ ())
+ auto_ (c);
- constraints (m);
- reference (m);
+ if (!c.default_ ().empty ())
+ os << " DEFAULT " << c.default_ ();
- return true;
+ if (!c.options ().empty ())
+ os << " " << c.options ();
}
virtual void
- type (semantics::data_member& m)
+ type (sema_rel::column& c, bool /*auto*/)
{
- os << column_type (m, prefix_);
+ os << c.type ();
}
virtual void
- null (semantics::data_member& m)
+ null (sema_rel::column& c)
{
- if (!context::null (m, prefix_))
+ if (!c.null ())
os << " NOT NULL";
}
virtual void
- default_null (semantics::data_member&)
+ primary_key ()
{
- os << " DEFAULT NULL";
+ os << " PRIMARY KEY";
}
virtual void
- default_bool (semantics::data_member&, bool v)
+ auto_ (sema_rel::column&)
{
- // Most databases do not support boolean literals. Those that
- // do should override this.
- //
- os << " DEFAULT " << (v ? "1" : "0");
}
- virtual void
- default_integer (semantics::data_member&, unsigned long long v, bool neg)
- {
- os << " DEFAULT " << (neg ? "-" : "") << v;
- }
+ protected:
+ schema_format format_;
+ create_table& create_table_;
+ bool first_;
+ };
- virtual void
- default_float (semantics::data_member&, double v)
- {
- os << " DEFAULT " << v;
- }
+ struct create_primary_key: trav_rel::primary_key, virtual context
+ {
+ typedef create_primary_key base;
- virtual void
- default_string (semantics::data_member&, string const& v)
+ create_primary_key (schema_format f, create_table& ct)
+ : format_ (f), create_table_ (ct)
{
- os << " DEFAULT " << quote_string (v);
}
virtual void
- default_enum (semantics::data_member&,
- tree /*enumerator*/,
- string const& /*name*/)
+ traverse (sema_rel::primary_key& pk)
{
- // Has to be implemented by the database-specific override.
+ // Single-column primary keys are generated inline in the
+ // column declaration.
//
- assert (false);
- }
+ if (pk.contains_size () == 1)
+ return;
- virtual void
- constraints (semantics::data_member& m)
- {
- if (m.count ("id"))
- os << " PRIMARY KEY";
+ // We will always follow a column.
+ //
+ os << "," << endl
+ << endl;
+
+ create (pk);
}
virtual void
- reference (semantics::data_member& m)
+ create (sema_rel::primary_key& pk)
{
- if (semantics::class_* c = object_pointer (member_utype (m, prefix_)))
- {
- os << " REFERENCES " << table_qname (*c) << " (" <<
- column_qname (*id_member (*c)) << ")";
- }
- else if (prefix_ == "id")
+ 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)
{
- // Container id column references the object table. It also
- // cascades on delete so that we can delete the object with
- // a single delete statement (needed for erase_query()).
- //
- semantics::class_& c (*context::top_object);
-
- os << " REFERENCES " << table_qname (c) << " (" <<
- column_qname (*id_member (c)) << ") ON DELETE CASCADE";
+ if (pk.contains_size () > 1)
+ {
+ if (i != pk.contains_begin ())
+ os << ",";
+
+ os << endl
+ << " ";
+ }
+
+ os << quote_id (i->column ().name ());
}
- }
- protected:
- void
- default_ (semantics::data_member&);
+ os << ")";
+ }
protected:
- string prefix_;
+ schema_format format_;
+ create_table& create_table_;
};
- struct create_common: virtual context
+ struct create_foreign_key: trav_rel::foreign_key, virtual context
{
- virtual void
- create_table_pre (string const& table)
- {
- os << "CREATE TABLE " << quote_id (table) << " (" << endl;
- }
+ typedef create_foreign_key base;
- virtual void
- create_table_post ()
+ create_foreign_key (schema_format f, create_table& ct)
+ : format_ (f), create_table_ (ct)
{
- os << ")" << endl;
}
virtual void
- create_index (string const& table, string const& column)
+ traverse (sema_rel::foreign_key& fk)
{
- os << "CREATE INDEX " << quote_id (table + '_' + column) << endl
- << " ON " << quote_id (table) << " (" << quote_id (column) << ")"
+ // We will always follow a column or another key.
+ //
+ os << "," << endl
<< endl;
- }
- };
-
- struct member_create: object_members_base, common, virtual create_common
- {
- typedef member_create base;
- member_create (emitter& e, ostream& os, std::vector<tables>& t)
- : object_members_base (false, true, false),
- common (e, os),
- tables_ (t)
- {
- }
-
- void
- pass (unsigned short p)
- {
- pass_ = p;
+ create (fk);
}
virtual void
- traverse_container (semantics::data_member& m, semantics::type& t)
+ create (sema_rel::foreign_key& fk)
{
- using semantics::type;
- using semantics::data_member;
+ using sema_rel::foreign_key;
- // Ignore inverse containers of object pointers.
- //
- if (inverse (m, "value"))
- return;
+ os << " CONSTRAINT " << quote_id (name (fk)) << endl
+ << " FOREIGN KEY (";
- container_kind_type ck (container_kind (t));
- type& vt (container_vt (t));
-
- string const& name (table_name (m, table_prefix_));
-
- if (tables_[pass_].count (name))
- return;
-
- pre_statement ();
- create_table_pre (name);
-
- // object_id (simple value)
- //
- string id_name (column_name (m, "id", "object_id"));
+ for (foreign_key::contains_iterator i (fk.contains_begin ());
+ i != fk.contains_end ();
+ ++i)
{
- instance<object_columns> oc ("id");
- oc->traverse_column (m, id_name, true);
- }
+ if (fk.contains_size () > 1)
+ {
+ if (i != fk.contains_begin ())
+ os << ",";
- // index (simple value)
- //
- string index_name;
- bool ordered (ck == ck_ordered && !unordered (m));
- if (ordered)
- {
- os << "," << endl;
+ os << endl
+ << " ";
+ }
- instance<object_columns> oc ("index");
- index_name = column_name (m, "index", "index");
- oc->traverse_column (m, index_name, true);
+ os << quote_id (i->column ().name ());
}
- // key (simple or composite value)
- //
- if (ck == ck_map || ck == ck_multimap)
- {
- type& kt (container_kt (t));
+ os << ")" << endl
+ << " REFERENCES " << quote_id (fk.referenced_table ()) << " (";
- os << "," << endl;
+ foreign_key::columns const& refs (fk.referenced_columns ());
- if (semantics::class_* ckt = composite_wrapper (kt))
- {
- instance<object_columns> oc;
- oc->traverse (m, *ckt, "key", "key");
- }
- else
+ for (foreign_key::columns::const_iterator i (refs.begin ());
+ i != refs.end ();
+ ++i)
+ {
+ if (refs.size () > 1)
{
- instance<object_columns> oc ("key");
- string const& name (column_name (m, "key", "key"));
- oc->traverse_column (m, name, true);
+ if (i != refs.begin ())
+ os << ",";
+
+ os << endl
+ << " ";
}
+
+ os << quote_id (*i);
}
- // value (simple or composite value)
- //
- {
- os << "," << endl;
+ os << ")";
- if (semantics::class_* cvt = composite_wrapper (vt))
- {
- instance<object_columns> oc;
- oc->traverse (m, *cvt, "value", "value");
- }
- else
- {
- instance<object_columns> oc ("value");
- string const& name (column_name (m, "value", "value"));
- oc->traverse_column (m, name, true);
- }
- }
+ if (fk.on_delete () != foreign_key::no_action)
+ on_delete (fk.on_delete ());
- create_table_post ();
- post_statement ();
+ if (fk.deferred ())
+ deferred ();
+ }
- tables_[pass_].insert (name);
+ virtual string
+ name (sema_rel::foreign_key& fk)
+ {
+ return fk.name ();
+ }
- // Create indexes.
- //
- pre_statement ();
- create_index (name, id_name);
- post_statement ();
+ virtual void
+ on_delete (sema_rel::foreign_key::action a)
+ {
+ using sema_rel::foreign_key;
- if (ordered)
+ switch (a)
{
- pre_statement ();
- create_index (name, index_name);
- post_statement ();
+ case foreign_key::cascade:
+ {
+ os << endl
+ << " ON DELETE CASCADE";
+ break;
+ }
+ case foreign_key::no_action:
+ break;
}
}
+ virtual void
+ deferred ()
+ {
+ os << endl
+ << " DEFERRABLE INITIALLY DEFERRED";
+ }
+
protected:
- std::vector<tables>& tables_;
- unsigned short pass_;
+ schema_format format_;
+ create_table& create_table_;
};
- struct class_create: traversal::class_, common, virtual create_common
+ struct create_table: trav_rel::table, common
{
- typedef class_create base;
+ typedef create_table base;
- class_create (emitter& e)
- : common (e, os_), os_ (e), member_create_ (e, os_, tables_)
+ create_table (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
{
- tables_.push_back (tables ()); // Dummy entry.
}
- class_create (class_create const& x)
- : root_context (), //@@ -Wextra
- context (),
- common (x.e_, os_),
- os_ (x.e_),
- member_create_ (x.e_, os_, tables_)
+ virtual void
+ create_pre (string const& table)
{
- tables_.push_back (tables ()); // Dummy entry.
+ os << "CREATE TABLE " << quote_id (table) << " (" << endl;
+ }
+
+ virtual void
+ create_post ()
+ {
+ os << ")" << endl;
+ }
+
+ virtual void
+ traverse (sema_rel::table& t)
+ {
+ // By default we do everything in a single pass. But some
+ // databases may require the second pass.
+ //
+ if (pass_ > 1)
+ return;
+
+ pre_statement ();
+ create_pre (t.name ());
+
+ instance<create_column> c (format_, *this);
+ instance<create_primary_key> pk (format_, *this);
+ instance<create_foreign_key> fk (format_, *this);
+ trav_rel::names n;
+
+ n >> c;
+ n >> pk;
+ n >> fk;
+
+ names (t, n);
+
+ create_post ();
+ post_statement ();
}
void
pass (unsigned short p)
{
pass_ = p;
+ }
- if (tables_.size () == pass_)
- tables_.push_back (tables ());
+ protected:
+ schema_format format_;
+ unsigned short pass_;
+ };
- member_create_->pass (p);
+ struct create_index: trav_rel::index, common
+ {
+ typedef create_index base;
+
+ create_index (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
+ {
}
virtual void
- traverse (type& c)
+ traverse (sema_rel::index& in)
{
// By default we do everything in a single pass. But some
// databases may require the second pass.
@@ -575,43 +579,83 @@ namespace relational
if (pass_ > 1)
return;
- if (c.file () != unit.file ())
- return;
+ pre_statement ();
+ create (in);
+ post_statement ();
+ }
- if (!object (c) || abstract (c))
- return;
+ virtual void
+ create (sema_rel::index& in)
+ {
+ using sema_rel::index;
- string const& name (table_name (c));
+ os << "CREATE INDEX " << quote_id (in.name ()) << endl
+ << " ON " << quote_id (in.table ().name ()) << " (";
- // If the table with this name was already created, assume the
- // user knows what they are doing and skip it.
- //
- if (tables_[pass_].count (name))
- return;
+ for (index::contains_iterator i (in.contains_begin ());
+ i != in.contains_end ();
+ ++i)
+ {
+ if (in.contains_size () > 1)
+ {
+ if (i != in.contains_begin ())
+ os << ",";
- pre_statement ();
- create_table_pre (name);
+ os << endl
+ << " ";
+ }
- {
- instance<object_columns> oc;
- oc->traverse (c);
+ os << quote_id (i->column ().name ());
}
- create_table_post ();
- post_statement ();
+ os << ")" << endl;
+ }
- tables_[pass_].insert (name);
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
+ }
- // Create tables for members.
- //
- member_create_->traverse (c);
+ protected:
+ schema_format format_;
+ unsigned short pass_;
+ };
+
+ struct create_model: trav_rel::model, common
+ {
+ typedef create_model base;
+
+ create_model (emitter_type& e, ostream& os, schema_format f)
+ : common (e, os), format_ (f)
+ {
+ }
+
+ // This version is only called for file schema.
+ //
+ virtual void
+ traverse (sema_rel::model& m)
+ {
+ traverse (m.names_begin (), m.names_end ());
+ }
+
+ virtual void
+ traverse (sema_rel::model::names_iterator begin,
+ sema_rel::model::names_iterator end)
+ {
+ for (; begin != end; ++begin)
+ dispatch (*begin);
+ }
+
+ void
+ pass (unsigned short p)
+ {
+ pass_ = p;
}
protected:
- emitter_ostream os_;
+ schema_format format_;
unsigned short pass_;
- std::vector<tables> tables_; // Seperate table for each pass.
- instance<member_create> member_create_;
};
}
}