// file : odb/relational/model.hxx // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #ifndef ODB_RELATIONAL_MODEL_HXX #define ODB_RELATIONAL_MODEL_HXX #include #include #include #include #include #include #include namespace relational { namespace model { typedef std::set tables; struct object_columns: object_columns_base, virtual context { typedef object_columns base; object_columns (sema_rel::model& model, sema_rel::table& table, string const& prefix = string ()) : model_ (model), table_ (table), prefix_ (prefix), id_override_ (false) { } virtual void traverse_object (semantics::class_& c) { if (context::top_object != &c) { // We are in one of the bases. Set the id_prefix to its // (unqualified) name. // string t (id_prefix_); id_prefix_ = class_name (c) + "::"; object_columns_base::traverse_object (c); id_prefix_ = t; } else object_columns_base::traverse_object (c); } virtual void traverse_composite (semantics::data_member* m, semantics::class_& c) { string t (id_prefix_); if (m != 0) // Member of a composite type. Add the data member to id_prefix. // if (!id_override_) id_prefix_ += m->name () + "."; else id_override_ = false; else // Composite base. Add its unqualified name to id_prefix. // id_prefix_ += class_name (c) + "::"; object_columns_base::traverse_composite (m, c); id_prefix_ = t; } virtual void traverse (semantics::data_member& m, semantics::class_& c, std::string const& kp, std::string const& dn) { // This overrides the member name for a composite container value // or key. // if (!kp.empty ()) { id_prefix_ = kp + "."; id_override_ = true; } object_columns_base::traverse (m, c, kp, dn); } using object_columns_base::traverse; virtual bool traverse_column (semantics::data_member& m, string const& name, bool) { // Ignore inverse object pointers. // if (inverse (m)) return false; string id (id_prefix_ + (prefix_.empty () ? m.name () : prefix_)); sema_rel::column& c ( model_.new_node ( id, column_type (m, prefix_), context::null (m, prefix_))); c.set ("cxx-node", static_cast (&m)); model_.new_edge (table_, c, name); // An id member cannot have a default value. // if (!context::id (m)) { string const& d (default_ (m)); if (!d.empty ()) c.default_ (d); } // If we have options, add them. // string const& o (column_options (m, prefix_)); if (!o.empty ()) c.options (o); constraints (m, name, id, c); reference (m, name, id, c); return true; } virtual string default_null (semantics::data_member&) { return "NULL"; } virtual string default_bool (semantics::data_member&, bool v) { // Most databases do not support boolean literals. Those that // do should override this. // return (v ? "1" : "0"); } virtual string default_integer (semantics::data_member&, unsigned long long v, bool neg) { std::ostringstream ostr; ostr << (neg ? "-" : "") << v; return ostr.str (); } virtual string default_float (semantics::data_member&, double v) { std::ostringstream ostr; ostr << v; return ostr.str (); } virtual string default_string (semantics::data_member&, string const& v) { return quote_string (v); } virtual string default_enum (semantics::data_member&, tree /*enumerator*/, string const& /*name*/) { // Has to be implemented by the database-specific override. // assert (false); } virtual void constraints (semantics::data_member& m, string const& /* name */, string const& /* id */, sema_rel::column& c) { if (!id (m)) return; sema_rel::primary_key& pk ( model_.new_node (m.count ("auto"))); pk.set ("cxx-node", static_cast (&m)); model_.new_edge (pk, c); // In most databases the primary key constraint can be manipulated // without an explicit name. So we use the special empty name for // primary keys in order not to clash with columns and other // constraints. If the target database does not support unnamed // primary key manipulation, then the database-specific code will // have to come up with a suitable name. // model_.new_edge (table_, pk, ""); } virtual void reference (semantics::data_member& m, string const& name, string const& id, sema_rel::column& c) { semantics::class_* p (object_pointer (member_utype (m, prefix_))); if (p == 0) return; sema_rel::foreign_key& fk ( model_.new_node ( id, table_name (*p), true)); // deferred fk.set ("cxx-node", static_cast (&m)); fk.referenced_columns ().push_back (column_name (*id_member (*p))); model_.new_edge (fk, c); // Derive the constraint name. Generally, we want it to be based // on the column name. This is straightforward for single column // references. In case of the composite ids, we will need to use // the column prefix which is based on the data member name, // unless overridden (see how the column pragma works for members // of composite value types). @@ This is a TODO. Perhaps use the // up-to-and-including composite member prefix? Though it can be // empty. // model_.new_edge (table_, fk, name + "_fk"); } protected: string default_ (semantics::data_member&); protected: sema_rel::model& model_; sema_rel::table& table_; string prefix_; string id_prefix_; bool id_override_; }; struct member_create: object_members_base, virtual context { typedef member_create base; member_create (sema_rel::model& model) : object_members_base (false, true, false), model_ (model) { } virtual void traverse_object (semantics::class_& c) { if (context::top_object != &c) { // We are in one of the bases. Set the id_prefix to its // (unqualified) name. // string t (id_prefix_); id_prefix_ = class_name (c) + "::"; object_members_base::traverse_object (c); id_prefix_ = t; } else { // Top-level object. Set its id as a prefix. // id_prefix_ = string (class_fq_name (c), 2) + "::"; object_members_base::traverse_object (c); } } virtual void traverse_composite (semantics::data_member* m, semantics::class_& c) { string t (id_prefix_); if (m != 0) // Member of a composite type. Add the data member to id_prefix. // id_prefix_ += m->name () + "."; else // Composite base. Add its unqualified name to id_prefix. // id_prefix_ += class_name (c) + "::"; object_members_base::traverse_composite (m, c); id_prefix_ = t; } virtual void traverse_container (semantics::data_member& m, semantics::type& ct) { using semantics::type; using semantics::data_member; // Ignore inverse containers of object pointers. // if (inverse (m, "value")) return; container_kind_type ck (container_kind (ct)); type& vt (container_vt (ct)); qname const& name (table_name (m, table_prefix_)); // Add the [] decorator to distinguish this id from non-container // ids (we don't want to ever end up comparing, for example, an // object table to a container table). // string id (id_prefix_ + m.name () + "[]"); sema_rel::container_table& t ( model_.new_node (id)); t.set ("cxx-node", static_cast (&m)); model_.new_edge (model_, t, name); // object_id (simple value, for now) // string id_name (column_name (m, "id", "object_id")); { instance oc (model_, t, "id"); oc->traverse_column (m, id_name, true); } // Foreign key for the object id. // { sema_rel::foreign_key& fk ( model_.new_node ( id + ".id", table_name (*context::top_object), false, // immediate sema_rel::foreign_key::cascade)); fk.set ("cxx-node", static_cast (&m)); fk.referenced_columns ().push_back ( column_name ( *id_member (*context::top_object))); // All the columns we have in this table so far are for the // object id. // for (sema_rel::table::names_iterator i (t.names_begin ()); i != t.names_end (); ++i) model_.new_edge ( fk, dynamic_cast (i->nameable ())); // Derive the constraint name. See the comment for the other // foreign key code above. // model_.new_edge (t, fk, id_name + "_fk"); } // index (simple value) // string index_name; bool ordered (ck == ck_ordered && !unordered (m)); if (ordered) { instance oc (model_, t, "index"); index_name = column_name (m, "index", "index"); oc->traverse_column (m, index_name, true); } // key (simple or composite value) // if (ck == ck_map || ck == ck_multimap) { type& kt (container_kt (ct)); if (semantics::class_* ckt = composite_wrapper (kt)) { instance oc (model_, t); oc->traverse (m, *ckt, "key", "key"); } else { instance oc (model_, t, "key"); string const& name (column_name (m, "key", "key")); oc->traverse_column (m, name, true); } } // value (simple or composite value) // { if (semantics::class_* cvt = composite_wrapper (vt)) { instance oc (model_, t); oc->traverse (m, *cvt, "value", "value"); } else { instance oc (model_, t, "value"); string const& name (column_name (m, "value", "value")); oc->traverse_column (m, name, true); } } // Create indexes. // using sema_rel::index; using sema_rel::column; { index& i (model_.new_node (id + ".id")); i.set ("cxx-node", static_cast (&m)); model_.new_edge ( i, dynamic_cast (t.find (id_name)->nameable ())); //@@ Once id can be composite, we need to revise this (see // a comment for the foreign key generation above). // model_.new_edge ( model_, i, name + "_" + id_name + "_i"); } if (ordered) { index& i (model_.new_node (id + ".index")); i.set ("cxx-node", static_cast (&m)); model_.new_edge ( i, dynamic_cast (t.find (index_name)->nameable ())); // This is always a single column (simple value). // model_.new_edge ( model_, i, name + "_" + index_name + "_i"); } } protected: sema_rel::model& model_; string id_prefix_; }; struct class_: traversal::class_, virtual context { typedef class_ base; class_ (sema_rel::model& model) : model_ (model) { } virtual void traverse (type& c) { if (class_file (c) != unit.file ()) return; if (!object (c) || abstract (c)) return; qname const& name (table_name (c)); // If the table with this name was already created, assume the // user knows what they are doing and skip it. // if (tables_.count (name)) { c.set ("model-range-first", model_.names_end ()); c.set ("model-range-last", model_.names_end ()); return; } string id (class_fq_name (c), 2); // Remove leading '::'. sema_rel::object_table& t( model_.new_node (id)); t.set ("cxx-node", static_cast (&c)); model_.new_edge (model_, t, name); sema_rel::model::names_iterator begin (--model_.names_end ()); { instance oc (model_, t); oc->traverse (c); } tables_.insert (name); // Create tables for members. // { instance mc (model_); mc->traverse (c); } sema_rel::model::names_iterator end (--model_.names_end ()); c.set ("model-range-first", begin); c.set ("model-range-last", end); } protected: sema_rel::model& model_; tables tables_; }; } } #endif // ODB_RELATIONAL_MODEL_HXX