// file : odb/relational/validator.cxx // copyright : Copyright (c) 2009-2013 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file #include #include #include #include #include #include #include using namespace std; namespace relational { namespace { // // Pass 2. // struct data_member2: traversal::data_member, context { data_member2 (bool& valid) : valid_ (valid) { } virtual void traverse (type& m) { if (transient (m)) return; if (null (m)) { if (semantics::class_* c = composite_wrapper (utype (m))) { if (has_a (*c, test_container)) { os << m.file () << ":" << m.line () << ":" << m.column () << ":" << " error: composite member containing containers cannot " << "be null" << endl; os << c->file () << ":" << c->line () << ":" << c->column () << ": info: composite value type is defined here" << endl; valid_ = false; } } } // Check on-delete. // if (m.count ("on-delete")) { const char* kp (container (m) ? "value" : ""); location l (m.location ()); // Make sure it is a pointer. // if (!object_pointer (member_utype (m, kp))) { error (l) << "on_delete specified for non-object pointer" << endl; valid_ = false; } // Make sure it is not inverse. // if (inverse (m, kp)) { error (l) << "on_delete specified for inverse object " << "pointer" << endl; valid_ = false; } // Make sure the pointer is nullable if asked to set it to NULL. // using sema_rel::foreign_key; if (m.get ("on-delete") == foreign_key::set_null && !null (m, kp)) { error (l) << "set_null specified for non-nullable object " "pointer" << endl; valid_ = false; } } } bool& valid_; }; struct object_no_id_members: object_members_base { object_no_id_members (bool& valid) : object_members_base (false, false, true), valid_ (valid), dm_ (0) { } virtual void traverse_pointer (semantics::data_member& m, semantics::class_&) { if (inverse (m)) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: inverse object pointer member '" << member_prefix_ << m.name () << "' in an object without an object id" << endl; valid_ = false; } } virtual void traverse_container (semantics::data_member& m, semantics::type&) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: container member '" << member_prefix_ << m.name () << "' in an object without an object id" << endl; valid_ = false; } virtual void traverse_composite (semantics::data_member* m, semantics::class_& c) { semantics::data_member* old_dm (dm_); if (dm_ == 0) dm_ = m; object_members_base::traverse_composite (m, c); dm_ = old_dm; } private: bool& valid_; semantics::data_member* dm_; // Direct object data member. }; struct composite_id_members: object_members_base { composite_id_members (bool& valid) : object_members_base (false, false, true), valid_ (valid), dm_ (0) { } virtual void traverse_pointer (semantics::data_member& m, semantics::class_&) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: object pointer member '" << member_prefix_ << m.name () << "' in a composite value type that is used as an object id" << endl; valid_ = false; } virtual void traverse_simple (semantics::data_member& m) { if (readonly (member_path_, member_scope_)) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: readonly member '" << member_prefix_ << m.name () << "' in a composite value type that is used as an object id" << endl; valid_ = false; } } virtual void traverse_container (semantics::data_member& m, semantics::type&) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: container member '" << member_prefix_ << m.name () << "' in a composite value type that is used as an object id" << endl; valid_ = false; } virtual void traverse_composite (semantics::data_member* m, semantics::class_& c) { semantics::data_member* old_dm (dm_); if (dm_ == 0) dm_ = m; object_members_base::traverse_composite (m, c); dm_ = old_dm; } private: bool& valid_; semantics::data_member* dm_; // Direct composite member. }; struct view_members: object_members_base { view_members (bool& valid) : object_members_base (false, false, true), valid_ (valid), dm_ (0) { } virtual void traverse_simple (semantics::data_member& m) { if (object_pointer (utype (m))) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: view data member '" << member_prefix_ << m.name () << "' is an object pointer" << endl; os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << ": info: views cannot contain object pointers" << endl; valid_ = false; } } virtual void traverse_container (semantics::data_member& m, semantics::type&) { semantics::data_member& dm (dm_ != 0 ? *dm_ : m); os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << " error: view data member '" << member_prefix_ << m.name () << "' is a container" << endl; os << dm.file () << ":" << dm.line () << ":" << dm.column () << ":" << ": info: views cannot contain containers" << endl; valid_ = false; } virtual void traverse_composite (semantics::data_member* m, semantics::class_& c) { semantics::data_member* old_dm (dm_); if (dm_ == 0) dm_ = m; object_members_base::traverse_composite (m, c); dm_ = old_dm; } private: bool& valid_; semantics::data_member* dm_; // Direct view data member. }; struct class2: traversal::class_, context { class2 (bool& valid) : valid_ (valid), typedefs_ (true), data_member_ (valid), object_no_id_members_ (valid), composite_id_members_ (valid), view_members_ (valid) { *this >> defines_ >> *this; *this >> typedefs_ >> *this; data_member_names_ >> data_member_; } virtual void traverse (type& c) { class_kind_type ck (class_kind (c)); switch (ck) { case class_object: names (c); traverse_object (c); break; case class_view: names (c); traverse_view (c); break; case class_composite: names (c); traverse_composite (c); break; case class_other: break; } // Make sure indexes are not defined for anything other than objects. // if (c.count ("index") && ck != class_object) { indexes& ins (c.get ("index")); for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i) { error (i->loc) << "index definition on a non-persistent class" << endl; valid_ = false; } } } virtual void traverse_object (type& c) { semantics::data_member* id (id_member (c)); if (id != 0) { if (semantics::class_* cm = composite_wrapper (utype (*id))) { // Composite id cannot be auto. // if (auto_ (*id)) { os << id->file () << ":" << id->line () << ":" << id->column () << ": error: composite id cannot be automatically assigned" << endl; valid_ = false; } // Make sure we don't have any containers or pointers in this // composite value type. // if (valid_) { composite_id_members_.traverse (*cm); if (!valid_) os << id->file () << ":" << id->line () << ":" << id->column () << ": info: composite id is defined here" << endl; } // Check that the composite value type is default-constructible. // if (!cm->default_ctor ()) { os << cm->file () << ":" << cm->line () << ":" << cm->column () << ": error: composite value type that is used as object id " << "is not default-constructible" << endl; os << cm->file () << ":" << cm->line () << ":" << cm->column () << ": info: provide default constructor for this value type" << endl; os << id->file () << ":" << id->line () << ":" << id->column () << ": info: composite id is defined here" << endl; valid_ = false; } } } else { if (!abstract (c)) { // Make sure we don't have any containers or inverse pointers. // object_no_id_members_.traverse (c); } } names (c, data_member_names_); // Validate bulk operation support. // for (bool i (true); i && c.count ("bulk"); i = false) { location_t l (c.get ("bulk-location")); if (polymorphic (c)) { error (l) << "bulk operations on polymorphic objects are " "not supported" << endl; valid_ = false; break; } if (has_a (c, test_straight_container)) { error (l) << "bulk operations on objects with containers are " "not supported" << endl; valid_ = false; break; } bool update (true); // Unless we only have manually-updated sections, we cannot generate // the bulk update operation. // user_sections& uss (c.get ("user-sections")); for (user_sections::iterator i (uss.begin ()); update && i != uss.end (); ++i) { const user_section& s (*i); // Skip special sections. // if (s.special != user_section::special_ordinary) continue; // Always-updated section still needs a separate statement // (since it may not be loaded). // if (!s.update_empty () && s.update != user_section::update_manual) update = false; } c.set ("bulk-persist", true); if (update) c.set ("bulk-update", true); c.set ("bulk-erase", true); } // Validate indexes. // { indexes& ins (c.get ("index")); // Make sure index members are not transient, inverse, or // containers. // for (indexes::iterator i (ins.begin ()); i != ins.end (); ++i) { index& in (*i); for (index::members_type::iterator i (in.members.begin ()); i != in.members.end (); ++i) { index::member& im (*i); semantics::data_member& m (*im.path.back ()); if (transient (m)) { error (im.loc) << "index member is transient" << endl; valid_ = false; } if (inverse (m)) { error (im.loc) << "index member is an inverse object " << "pointer" << endl; valid_ = false; } if (container (m)) { error (im.loc) << "index member is a container" << endl; valid_ = false; } } } } } virtual void traverse_view (type& c) { const view_query& vq (c.get ("query")); // Make sure we don't have any containers or object pointers. // view_members_.traverse (c); names (c, data_member_names_); // Allow certain kinds of empty views. // if (vq.kind != view_query::runtime && vq.kind != view_query::complete_execute) { // Allow all the members to be deleted. // column_count_type const& cc (column_count (c)); if (cc.total == 0) { os << c.file () << ":" << c.line () << ":" << c.column () << ":" << " error: no persistent data members in the class" << endl; valid_ = false; } } } virtual void traverse_composite (type& c) { names (c, data_member_names_); } public: bool& valid_; traversal::defines defines_; typedefs typedefs_; data_member2 data_member_; traversal::names data_member_names_; object_no_id_members object_no_id_members_; composite_id_members composite_id_members_; view_members view_members_; }; } void validator:: validate (options const&, features&, semantics::unit& u, semantics::path const&, unsigned short pass) { bool valid (true); // Validate custom type mapping. // if (pass == 1) { // Create an empty list if we don't have one. This makes the // rest of the code simpler. // if (!u.count ("custom-db-types")) u.set ("custom-db-types", custom_db_types ()); custom_db_types & cts (u.get ("custom-db-types")); for (custom_db_types::iterator i (cts.begin ()); i != cts.end (); ++i) { custom_db_type& ct (*i); if (ct.type.empty ()) { error (ct.loc) << "'type' clause expected in db pragma map" << endl; valid = false; } if (ct.as.empty ()) { error (ct.loc) << "'as' clause expected in db pragma map" << endl; valid = false; } if (ct.to.empty ()) ct.to = "(?)"; else { size_t p (ct.to.find ("(?)")); if (p == string::npos) { error (ct.loc) << "no '(?)' expression in the 'to' clause " << "of db pragma map" << endl; valid = false; } else if (ct.to.find ("(?)", p + 3) != string::npos) { error (ct.loc) << "multiple '(?)' expressions in the 'to' " << "clause of db pragma map" << endl; valid = false; } } if (ct.from.empty ()) ct.from = "(?)"; else { size_t p (ct.from.find ("(?)")); if (p == string::npos) { error (ct.loc) << "no '(?)' expression in the 'from' clause " << "of db pragma map" << endl; valid = false; } else if (ct.from.find ("(?)", p + 3) != string::npos) { error (ct.loc) << "multiple '(?)' expressions in the 'from' " << "clause of db pragma map" << endl; valid = false; } } } } if (!valid) throw failed (); if (pass == 1) { } else { traversal::unit unit; traversal::defines unit_defines; typedefs unit_typedefs (true); traversal::namespace_ ns; class2 c (valid); unit >> unit_defines >> ns; unit_defines >> c; unit >> unit_typedefs >> c; traversal::defines ns_defines; typedefs ns_typedefs (true); ns >> ns_defines >> ns; ns_defines >> c; ns >> ns_typedefs >> c; unit.dispatch (u); } if (!valid) throw failed (); } }