summaryrefslogtreecommitdiff
path: root/odb/odb/relational/validator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/relational/validator.cxx')
-rw-r--r--odb/odb/relational/validator.cxx638
1 files changed, 638 insertions, 0 deletions
diff --git a/odb/odb/relational/validator.cxx b/odb/odb/relational/validator.cxx
new file mode 100644
index 0000000..50c887e
--- /dev/null
+++ b/odb/odb/relational/validator.cxx
@@ -0,0 +1,638 @@
+// file : odb/relational/validator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <map>
+#include <iostream>
+
+#include <odb/diagnostics.hxx>
+#include <odb/traversal.hxx>
+#include <odb/relational/common.hxx>
+#include <odb/relational/context.hxx>
+#include <odb/relational/validator.hxx>
+
+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 or a member with points_to pragma.
+ //
+ if (!object_pointer (utype (m, kp)) && !points_to (m))
+ {
+ 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<foreign_key::action_type> ("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_pointer (semantics::data_member& m, semantics::class_&)
+ {
+ if (dm_ != 0 && object_pointer (utype (m)))
+ {
+ location const& l (dm_->location ());
+
+ error (l) << "nested view data member '" << member_prefix_
+ << m.name () << "' is an object pointer" << endl;
+ info (l) << "views can only contain direct object pointer members"
+ << 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<indexes> ("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)
+ {
+ data_member_path* id (id_member (c));
+
+ if (id != 0)
+ {
+ if (semantics::class_* cm = composite_wrapper (utype (*id)))
+ {
+ location idl (id->front ()->location ());
+
+ // Composite id cannot be auto.
+ //
+ if (auto_ (*id))
+ {
+ error (idl) << "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_)
+ info (idl) << "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;
+
+ info (idl) << "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<location_t> ("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> ("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<indexes> ("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<view_query> ("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
+ 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> ("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 operation_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 operation_failed ();
+ }
+}