summaryrefslogtreecommitdiff
path: root/odb/validator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/validator.cxx')
-rw-r--r--odb/validator.cxx1910
1 files changed, 0 insertions, 1910 deletions
diff --git a/odb/validator.cxx b/odb/validator.cxx
deleted file mode 100644
index 64a539f..0000000
--- a/odb/validator.cxx
+++ /dev/null
@@ -1,1910 +0,0 @@
-// file : odb/validator.cxx
-// copyright : Copyright (c) 2009-2019 Code Synthesis Tools CC
-// license : GNU GPL v3; see accompanying LICENSE file
-
-#include <odb/gcc.hxx>
-
-#include <set>
-#include <iostream>
-
-#include <odb/traversal.hxx>
-#include <odb/common.hxx>
-#include <odb/context.hxx>
-#include <odb/diagnostics.hxx>
-#include <odb/validator.hxx>
-#include <odb/cxx-lexer.hxx>
-
-#include <odb/relational/validator.hxx>
-
-using namespace std;
-
-namespace
-{
- // Resolve null overrides.
- //
- static void
- override_null (semantics::node& n, string const& prefix = "")
- {
- string p (prefix.empty () ? prefix : prefix + '-');
-
- if (n.count (p + "null") && n.count (p + "not-null"))
- {
- if (n.get<location_t> (p + "null-location") <
- n.get<location_t> (p + "not-null-location"))
- {
- n.remove (p + "null");
- n.remove (p + "null-location");
- }
- else
- {
- n.remove (p + "not-null");
- n.remove (p + "not-null-location");
- }
- }
- }
-
- //
- // Pass 1.
- //
-
- struct data_member1: traversal::data_member, context
- {
- data_member1 (bool& valid)
- : valid_ (valid)
- {
- }
-
- virtual void
- traverse (type& m)
- {
- semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
- bool obj (object (c));
-
- // If the class is marked transient, then mark each non-virtual
- // data member as transient.
- //
- {
- bool t (transient (m));
-
- if (!t && c.count ("transient") && !m.count ("virtual"))
- {
- m.set ("transient", true);
- t = true;
- }
-
- if (t)
- return;
- }
-
- semantics::names* hint;
- semantics::type& t (utype (m, hint));
-
- if (t.fq_anonymous (hint))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: unnamed type in data member declaration" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " info: use 'typedef' to name this type" << endl;
-
- valid_ = false;
- }
-
- // Make sure id or inverse member is not marked readonly since we
- // depend on these three sets not having overlaps.
- //
- if (m.count ("readonly")) // context::readonly() also checks the class.
- {
- if (id (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: object id should not be declared readonly" << endl;
-
- valid_ = false;
- }
-
- if (inverse (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: inverse object pointer should not be declared "
- << "readonly" << endl;
-
- valid_ = false;
- }
- }
-
- // Make sure a member of a section is an immediate member of an object.
- // The same for the section member itself.
- //
- bool section (t.fq_name () == "::odb::section");
-
- if (!obj)
- {
- if (m.count ("section-member"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
- "error: data member belonging to a section can only be a " <<
- "direct member of a persistent class" << endl;
- valid_ = false;
- }
-
- if (section)
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": " <<
- "error: section data member can only be a direct member of a " <<
- "persistent class" << endl;
- valid_ = false;
- }
- }
-
- // Make sure the load and update pragmas are only specified on
- // section members.
- //
- if (!section)
- {
- if (m.count ("section-load"))
- {
- location_t loc (m.get<location_t> ("section-load-location"));
- error (loc) << "'#pragma db load' can only be specified for "
- "a section data member" << endl;
- valid_ = false;
- }
-
- if (m.count ("section-update"))
- {
- location_t loc (m.get<location_t> ("section-update-location"));
- error (loc) << "'#pragma db update' can only be specified for "
- "a section data member" << endl;
- valid_ = false;
- }
- }
-
- // Check that the addition version makes sense.
- //
- unsigned long long av (m.get<unsigned long long> ("added", 0));
- if (av != 0)
- {
- location_t l (m.get<location_t> ("added-location"));
-
- if (id (m))
- {
- error (l) << "object id cannod be soft-added" << endl;
- valid_ = false;
- }
-
- if (version (m))
- {
- error (l) << "optimistic concurrency version cannod be "
- "soft-added" << endl;
- valid_ = false;
- }
-
- if (!versioned ())
- {
- error (l) << "added data member in a non-versioned object " <<
- "model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (av > mv.current)
- {
- error (l) << "addition version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (av <= mv.base)
- {
- error (l) << "addition version is less than or equal to the " <<
- "base model version" << endl;
- info (l) << "delete this pragma since migration to version " <<
- av << " is no longer possible" << endl;
- valid_ = false;
- }
- }
- }
-
- // Check that the deletion version makes sense.
- //
- unsigned long long dv (m.get<unsigned long long> ("deleted", 0));
- if (dv != 0)
- {
- location_t l (m.get<location_t> ("deleted-location"));
-
- if (id (m))
- {
- error (l) << "object id cannod be soft-deleted" << endl;
- valid_ = false;
- }
-
- if (version (m))
- {
- error (l) << "optimistic concurrency version cannod be "
- "soft-deleted" << endl;
- valid_ = false;
- }
-
- if (!versioned ())
- {
- error (l) << "deleted data member in a non-versioned object " <<
- "model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (dv > mv.current)
- {
- error (l) << "deletion version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (dv <= mv.base)
- {
- error (l) << "deletion version is less than or equal to the " <<
- "base model version" << endl;
- info (c.location ()) << "delete this data member since " <<
- "migration to version " << dv << " is no longer possible" <<
- endl;
- valid_ = false;
- }
- }
- }
-
- // Make sure that addition and deletion versions are properly ordered.
- // We can have both the [av, dv] (added then deleted) and [dv, av]
- // (deleted then re-added) intervals.
- //
- if (av != 0 && dv != 0 && av == dv)
- {
- location_t al (m.get<location_t> ("added-location"));
- location_t dl (m.get<location_t> ("deleted-location"));
-
- error (al) << "addition and deletion versions are the same" << endl;
- info (dl) << "deletion version is specified here" << endl;
- valid_ = false;
- }
-
- if (section)
- return; // Section data member is transient.
-
- // Resolve null overrides.
- //
- override_null (m);
- override_null (m, "value");
- }
-
- bool& valid_;
- };
-
- // Find special members (id, version).
- //
- struct special_members: traversal::class_, context
- {
- special_members (class_kind_type kind,
- bool& valid,
- semantics::data_member*& id,
- semantics::data_member*& optimistic)
- : kind_ (kind), member_ (valid, id, optimistic)
- {
- if (kind != class_view)
- *this >> inherits_ >> *this;
-
- *this >> names_ >> member_;
- }
-
- virtual void
- traverse (semantics::class_& c)
- {
- // Skip transient bases.
- //
- switch (kind_)
- {
- case class_object:
- {
- if (!object (c))
- return;
- break;
- }
- case class_view:
- {
- break;
- }
- case class_composite:
- {
- if (!composite (c))
- return;
- break;
- }
- case class_other:
- {
- assert (false);
- break;
- }
- }
-
- // Views don't have bases.
- //
- if (kind_ != class_view)
- inherits (c);
-
- names (c);
- }
-
- private:
- struct member: traversal::data_member, context
- {
- member (bool& valid,
- semantics::data_member*& id,
- semantics::data_member*& optimistic)
- : valid_ (valid), id_ (id), optimistic_ (optimistic)
- {
- }
-
- virtual void
- traverse (semantics::data_member& m)
- {
- if (m.count ("id"))
- {
- if (id_ == 0)
- id_ = &m;
- else
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple object id members" << endl;
-
- os << id_->file () << ":" << id_->line () << ":" << id_->column ()
- << ": info: previous id member is declared here" << endl;
-
- valid_ = false;
- }
- }
-
- if (version (m))
- {
- if (optimistic_ == 0)
- optimistic_ = &m;
- else
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: multiple version members" << endl;
-
- semantics::data_member& o (*optimistic_);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": info: previous version member is declared here" << endl;
-
- valid_ = false;
- }
- }
- }
-
- bool& valid_;
- semantics::data_member*& id_;
- semantics::data_member*& optimistic_;
- };
-
- class_kind_type kind_;
- member member_;
- traversal::names names_;
- traversal::inherits inherits_;
- };
-
- //
- //
- struct value_type: traversal::type, context
- {
- virtual void
- traverse (semantics::type& t)
- {
- // Resolve null overrides.
- //
- override_null (t);
- override_null (t, "value");
- }
- };
-
- struct typedefs1: typedefs
- {
- typedefs1 (traversal::declares& d)
- : typedefs (true), declares_ (d)
- {
- }
-
- void
- traverse (semantics::typedefs& t)
- {
- if (check (t))
- traversal::typedefs::traverse (t);
- else
- declares_.traverse (t);
- }
-
- private:
- traversal::declares& declares_;
- };
-
- //
- //
- struct class1: traversal::class_, context
- {
- class1 (bool& valid)
- : valid_ (valid), typedefs_ (declares_), member_ (valid)
- {
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
- *this >> declares_ >> vt_;
-
- names_member_ >> member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck != class_other)
- {
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end (); ++i)
- {
- type& b (i->base ());
-
- if (class_kind (b) == class_other)
- continue;
-
- location_t cl;
- location_t bl;
-
- // If we are in the same file, then use real locations (as
- // opposed to definition locations) since that's the order
- // in which we will be generating the code.
- //
- if (class_file (c) == class_file (b))
- {
- cl = class_real_location (c);
- bl = class_real_location (b);
- }
- else
- {
- cl = class_location (c);
- bl = class_location (b);
- }
-
- if (cl < bl)
- {
- // We cannot use class_name() for b since it might not have
- // yet been processed (tree-hint).
- //
- error (cl) << "base class " << b.name () << " must "
- << "be defined before derived class " << class_name (c)
- << endl;
-
- info (bl) << "class " << b.name () << " is define here"
- << endl;
-
- valid_ = false;
- }
- }
- }
-
- 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);
- // Fall through.
- case class_other:
- vt_.dispatch (c);
- break;
- }
- }
-
- virtual void
- traverse_object (type& c)
- {
- // Check that the deletion version makes sense.
- //
- if (unsigned long long v = c.get<unsigned long long> ("deleted", 0))
- {
- location_t l (c.get<location_t> ("deleted-location"));
-
- if (!versioned ())
- {
- error (l) << "deleted class in a non-versioned object model" << endl;
- valid_ = false;
- }
- else
- {
- model_version const& mv (version ());
-
- if (v > mv.current)
- {
- error (l) << "deletion version is greater than the current " <<
- "model version" << endl;
- valid_ = false;
- }
- else if (v <= mv.base)
- {
- error (l) << "deletion version is less than or equal to the " <<
- "base model version" << endl;
- info (c.location ()) << "delete this class since migration to " <<
- "version " << v << " is no longer possible" << endl;
- valid_ = false;
- }
- }
- }
-
- // Check that the callback function exist.
- //
- if (c.count ("callback"))
- {
- string name (c.get<string> ("callback"));
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (name.c_str ()), false, false));
-
- if (decl != error_mark_node && TREE_CODE (decl) == BASELINK)
- {
- // Figure out if we have a const version of the callback. OVL_*
- // macros work for both FUNCTION_DECL and OVERLOAD.
- //
-#if BUILDING_GCC_MAJOR >= 8
- for (ovl_iterator i (BASELINK_FUNCTIONS (decl)); i; ++i)
-#else
- for (tree o (BASELINK_FUNCTIONS (decl)); o != 0; o = OVL_NEXT (o))
-#endif
- {
-#if BUILDING_GCC_MAJOR >= 8
- tree f (*i);
-#else
- tree f (OVL_CURRENT (o));
-#endif
- if (DECL_CONST_MEMFUNC_P (f))
- {
- c.set ("callback-const", true);
- break;
- }
- }
-
- //@@ Would be nice to check the signature of the function(s)
- // instead of postponing it until the C++ compilation. Though
- // we may still get C++ compilation errors because of const
- // mismatch.
- //
- }
- else
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ": "
- << "error: unable to resolve member function '" << name << "' "
- << "specified with '#pragma db callback' for class '"
- << class_name (c) << "'" << endl;
-
- valid_ = false;
- }
- }
-
- // Check bases.
- //
- type* poly_root (0);
-
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b))
- {
- if (type* r = polymorphic (b))
- {
- if (poly_root == 0)
- {
- poly_root = r;
- c.set ("polymorphic-base", &static_cast<semantics::class_&> (b));
- }
- // If poly_root and r are the same, then we have virtual
- // inheritance. Though we don't support it at the moment.
- //
- else //if (poly_root != r)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: persistent class '" << class_name (c) << "' "
- << "derives from multiple polymorphic bases" << endl;
-
- type& a (*poly_root);
- os << a.file () << ":" << a.line () << ":" << a.column () << ":"
- << " info: first polymorphic base defined here" << endl;
-
- type& b (*r);
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: second polymorphic base defined here" << endl;
-
- valid_ = false;
- }
- }
- }
- else if (view (b) || composite (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or value type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object types cannot derive from view or value "
- << "types"
- << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check special members.
- //
- using semantics::data_member;
-
- data_member* id (0);
- data_member* optimistic (0);
- {
- special_members t (class_object, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id == 0)
- {
- // An object without an id should either be reuse-abstract
- // or explicitly marked as such. We check that it is not
- // polymorphic below.
- //
- if (!(c.count ("id") || abstract (c)))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no data member designated as an object id" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db id' to specify an object id member"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: or explicitly declare that this persistent class "
- << "has no object id with '#pragma db object no_id'" << endl;
-
- valid_ = false;
- }
- }
- else
- {
- // Convert id to a member path. This has to happen early since
- // a lot of code that runs next (e.g., processor, pass 1) depends
- // on this information being available.
- //
- data_member_path& idp (c.set ("id-member", data_member_path ()));
- idp.push_back (id);
-
- // See if we have a member path that we need to resolve.
- //
- const string& name (id->get<string> ("id"));
- location_t l (id->get<location_t> ("id-location"));
-
- if (!name.empty ())
- {
- if (id->count ("auto"))
- {
- error (l) << "nested id cannot be automatically assigned" << endl;
- valid_ = false;
- }
-
- if (semantics::class_* comp = utype (*id).is_a<semantics::class_> ())
- {
- try
- {
- resolve_data_members (idp, *comp, name, l, lex_);
- }
- catch (const operation_failed&) {valid_ = false;}
- }
- else
- {
- error (l) << "nested id requires composite member" << endl;
- valid_ = false;
- }
-
- // Mark the whole member as readonly.
- //
- id->set ("readonly", true);
- }
-
- data_member* idf (idp.front ());
- data_member* idb (idp.back ());
-
- // Complain if an id member has a default value (default value
- // for the id's type is ok -- we will ignore it).
- //
- if (idb->count ("default"))
- {
- error (l) << "object id member cannot have default value" << endl;
- valid_ = false;
- }
-
- // Complain if an id member is in a section.
- //
- if (idf->count ("section-member"))
- {
- error (l) << "object id member cannot be in a section" << endl;
- valid_ = false;
- }
-
- // Automatically mark the id member as not null. If we already have
- // an explicit null pragma for this member, issue an error.
- //
- if (idb->count ("null"))
- {
- error (l) << "object id member cannot be null" << endl;
- valid_ = false;
- }
- else
- idf->set ("not-null", true);
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& m (*optimistic);
-
- // Make sure we have the class declared optimistic.
- //
- if (&m.scope () == &c && !c.count ("optimistic"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version data member in a class not declared "
- << "optimistic" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db optimistic' to declare this "
- << "class optimistic" << endl;
-
- valid_ = false;
- }
-
- // Make sure we have object id.
- //
- if (id == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without an object id" << endl;
-
- valid_ = false;
- }
-
- // Make sure id and version members are in the same class. The
- // current architecture relies on that.
- //
- if (id != 0 && &id->scope () != &m.scope ())
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: object id and version members are in different "
- << "classes" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: object id and version members must be in the same "
- << "class" << endl;
-
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": info: object id member is declared here" << endl;
-
- os << m.file () << ":" << m.line () << ":" << m.column () << ":"
- << " error: version member is declared here" << endl;
-
- valid_ = false;
- }
-
- // Make sure this class is not readonly.
- //
- if (readonly (c))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class cannot be readonly" << endl;
-
- valid_ = false;
- }
-
- // Complain if the version member is in a section.
- //
- if (m.count ("section-member"))
- {
- os << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: version member cannot be in a section" << endl;
- valid_ = false;
- }
-
- // This takes care of also marking derived classes as optimistic.
- //
- c.set ("optimistic-member", optimistic);
- }
- else
- {
- // Make sure there is a version member if the class declared
- // optimistic.
- //
- if (c.count ("optimistic"))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: optimistic class without a version member" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use '#pragma db version' to declare on of the "
- << "data members as a version" << endl;
-
- valid_ = false;
- }
- }
-
- // Polymorphic inheritance.
- //
- if (c.count ("polymorphic") && poly_root == 0)
- {
- // Root of the hierarchy.
- //
-
- if (id == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: polymorphic class without an object id" << endl;
-
- valid_ = false;
- }
-
- if (!TYPE_POLYMORPHIC_P (c.tree_node ()))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: non-polymorphic class (class without virtual "
- << "functions) cannot be declared polymorphic" << endl;
-
- valid_ = false;
- }
-
- poly_root = &c;
- }
-
- if (poly_root != 0)
- c.set ("polymorphic-root", poly_root);
-
- // Sectionable objects.
- //
- if (c.count ("sectionable"))
- {
- if (optimistic == 0)
- {
- location_t l (c.get<location_t> ("sectionable-location"));
- error (l) << "only optimistic class can be sectionable" << endl;
- valid_ = false;
- }
- else if (&optimistic->scope () != &c && poly_root != &c)
- {
- location l (c.get<location_t> ("sectionable-location"));
- error (l) << "only optimistic class that declares the version " <<
- "data member or that is a root of a polymorphic hierarchy can " <<
- "be sectionable" << endl;
- info (optimistic->location ()) << "version member is declared " <<
- "here" << endl;
- valid_ = false;
- }
- }
-
- // Update features set based on this object.
- //
- if (options.at_once () || class_file (c) == unit.file ())
- {
- if (poly_root != 0)
- features.polymorphic_object = true;
- else if (id == 0 && !abstract (c))
- features.no_id_object = true;
- else
- features.simple_object = true;
- }
- }
-
- virtual void
- traverse_view (type& c)
- {
- // Views require query support.
- //
- if (!options.generate_query ())
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: query support is required when using views"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: use the --generate-query option to enable query "
- << "support"
- << endl;
-
- valid_ = false;
- }
-
- // Check that the callback function exist.
- //
- if (c.count ("callback"))
- {
- string name (c.get<string> ("callback"));
- tree decl (
- lookup_qualified_name (
- c.tree_node (), get_identifier (name.c_str ()), false, false));
-
- if (decl == error_mark_node || TREE_CODE (decl) != BASELINK)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ": "
- << "error: unable to resolve member function '" << name << "' "
- << "specified with '#pragma db callback' for class '"
- << class_name (c) << "'" << endl;
-
- valid_ = false;
- }
-
- // No const version for views.
-
- //@@ Would be nice to check the signature of the function(s)
- // instead of postponing it until the C++ compilation. Though
- // we may still get C++ compilation errors because of const
- // mismatch.
- //
- }
-
- // Check bases.
- //
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b) || view (b) || composite (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is an object, "
- << "view, or value type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: view types cannot derive from view, object or "
- << "value types"
- << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check id.
- //
- semantics::data_member* id (0);
- semantics::data_member* optimistic (0);
- {
- special_members t (class_view, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id != 0)
- {
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: view type data member cannot be designated as an "
- << "object id" << endl;
-
- valid_ = false;
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& o (*optimistic);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: view type data member cannot be designated as a "
- << "version" << endl;
-
- valid_ = false;
- }
-
- // Update features set based on this view.
- //
- if (options.at_once () || class_file (c) == unit.file ())
- {
- features.view = true;
- }
- }
-
- virtual void
- traverse_composite (type& c)
- {
- for (type::inherits_iterator i (c.inherits_begin ());
- i != c.inherits_end ();
- ++i)
- {
- type& b (i->base ());
-
- if (object (b) || view (b))
- {
- // @@ Should we use hint here?
- //
- string name (class_fq_name (b));
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: base class '" << name << "' is a view or object "
- << "type"
- << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " info: composite value types cannot derive from object "
- << "or view types" << endl;
-
- os << b.file () << ":" << b.line () << ":" << b.column () << ":"
- << " info: class '" << name << "' is defined here" << endl;
-
- valid_ = false;
- }
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check id.
- //
- semantics::data_member* id (0);
- semantics::data_member* optimistic (0);
- {
- special_members t (class_composite, valid_, id, optimistic);
- t.traverse (c);
- }
-
- if (id != 0)
- {
- os << id->file () << ":" << id->line () << ":" << id->column ()
- << ": error: value type data member cannot be designated as an "
- << "object id" << endl;
-
- valid_ = false;
- }
-
- if (optimistic != 0)
- {
- semantics::data_member& o (*optimistic);
-
- os << o.file () << ":" << o.line () << ":" << o.column ()
- << ": error: value type data member cannot be designated as a "
- << "version" << endl;
-
- valid_ = false;
- }
- }
-
- bool& valid_;
-
- traversal::defines defines_;
- traversal::declares declares_;
- typedefs1 typedefs_;
-
- value_type vt_;
- data_member1 member_;
- traversal::names names_member_;
-
- cxx_string_lexer lex_;
- };
-
- //
- // Pass 2.
- //
-
- struct data_member2: traversal::data_member, context
- {
- data_member2 (bool& valid): valid_ (valid) {}
-
- // Verify that composite value 'c' that is used by data member
- // 'm' is defined before (or inside) class 's'.
- //
- void
- verify_composite_location (semantics::class_& c,
- semantics::class_& s,
- semantics::data_member& m)
- {
- if (c.in_scope (s))
- return;
-
- location_t cl;
- location_t sl;
-
- // If we are in the same file, then use real locations (as
- // opposed to definition locations) since that's the order
- // in which we will be generating the code.
- //
- if (class_file (c) == class_file (s))
- {
- cl = class_real_location (c);
- sl = class_real_location (s);
- }
- else
- {
- cl = class_location (c);
- sl = class_location (s);
- }
-
- if (sl < cl)
- {
- const string& cn (class_name (c));
-
- error (sl)
- << "composite value type " << class_fq_name (c) << " must "
- << "be defined before being used in class " << class_fq_name (s)
- << endl;
-
- info (m.location ())
- << "data member " << m.name () << " uses " << cn << endl;
-
- info (cl) << "class " << cn << " is define here" << endl;
-
- valid_ = false;
- }
- }
-
- virtual void
- traverse (type& m)
- {
- using semantics::class_;
-
- semantics::type& t (utype (m));
- class_& s (dynamic_cast<class_&> (m.scope ()));
-
- // Validate pointed-to objects.
- //
- class_* c;
- if ((c = object_pointer (t)) || (c = points_to (m)))
- {
- bool poly (polymorphic (*c));
- bool abst (abstract (*c));
-
- // Make sure the pointed-to class is complete.
- //
- if (!c->complete ())
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "is incomplete" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is declared here"
- << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: consider including its definition with the "
- << "--odb-epilogue option" << endl;
-
- valid_ = false;
- return;
- }
-
- // Make sure the pointed-to class is not reuse-abstract.
- //
- if (abst && !poly)
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "is abstract" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- throw operation_failed ();
- }
-
- // Make sure the pointed-to class has object id unless it is in a
- // view where we can load no-id objects.
- //
- if (data_member_path* id = id_member (*c))
- {
- semantics::type& idt (utype (*id));
-
- // Make sure composite id is defined before this pointer. Failed
- // that we get non-obvious C++ compiler errors in generated code.
- //
- if (class_* comp = composite_wrapper (idt))
- verify_composite_location (*comp, s, m);
- }
- else if (!view_member (m))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "has no object id" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- valid_ = false;
- return;
- }
-
- // Make sure the pointed-to class has a default ctor. Since we will
- // use database::load() in the generated code, lack of a default ctor
- // will lead to uncompilable generated code. Poly-abstract is Ok.
- //
- if (!c->default_ctor () && !(abst && poly))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: pointed-to class '" << class_fq_name (*c) << "' "
- << "has no default constructor" << endl;
-
- os << c->file () << ":" << c->line () << ":" << c->column () << ": "
- << "info: class '" << class_name (*c) << "' is defined here"
- << endl;
-
- valid_ = false;
- return;
- }
- }
-
- // Make sure composite type is defined before (or inside)
- // this class. Failed that we get non-obvious C++ compiler
- // errors in generated code.
- //
- if (class_* comp = composite_wrapper (t))
- verify_composite_location (*comp, s, m);
-
- // Check that containers with composite value element types don't have
- // any nested containers.
- //
- if (semantics::type* c = container (m))
- {
- class_* vt (0);
- class_* kt (0);
-
- switch (container_kind (*c))
- {
- case ck_map:
- case ck_multimap:
- {
- kt = composite_wrapper (container_kt (m));
- }
- // Fall through.
- default:
- {
- vt = composite_wrapper (container_vt (m));
- }
- }
-
- if (vt != 0 && has_a (*vt, test_container))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: containers of containers not supported" << endl;
-
- os << vt->file () << ":" << vt->line () << ":" << vt->column ()
- << ": info: composite element value " << class_fq_name (*vt)
- << " contains container(s)" << endl;
-
- valid_ = false;
- }
-
- if (kt != 0 && has_a (*kt, test_container))
- {
- os << m.file () << ":" << m.line () << ":" << m.column () << ": "
- << "error: containers of containers not supported" << endl;
-
- os << kt->file () << ":" << kt->line () << ":" << kt->column ()
- << ": info: composite element key " << class_fq_name (*kt)
- << " contains container(s)" << endl;
-
- valid_ = false;
- }
- }
- }
-
- bool& valid_;
- };
-
- // Make sure soft-delete versions make sense for dependent entities.
- // We don't seem to need anything for soft-add since if an entity is
- // not added (e.g., an object), then we cannot reference it in the
- // C++ sense (e.g., a pointer).
- //
- struct version_dependencies: object_members_base
- {
- version_dependencies (bool& valid): valid_ (valid) {}
-
- virtual void
- traverse_object (semantics::class_& c)
- {
- // For reuse inheritance we allow the base to be soft-deleted. In
- // a sense, it becomes like an abstract class with the added
- // functionality of being able to load old data.
- //
- // For polymorphism inheritance things are a lot more complicated
- // and we don't allow a base to be soft-deleted since there is a
- // link (foreign key) from the derived table.
- //
- semantics::class_* poly (polymorphic (c));
- if (poly != 0 && poly != &c)
- {
- semantics::class_& base (polymorphic_base (c));
- check_strict (
- c, base, "polymorphic derived object", "polymorphic base");
- }
-
- // Don't go into bases.
- //
- names (c);
- }
-
- virtual void
- traverse_simple (semantics::data_member& m)
- {
- semantics::class_& c (dynamic_cast<semantics::class_&> (m.scope ()));
-
- switch (class_kind (c))
- {
- case class_object:
- {
- // Member shouldn't be deleted after the object that contains it.
- //
- check_lax (m, c, "data member", "object");
- break;
- }
- // We leave it up to the user to make sure the view is consistent
- // with the database schema it is being run on.
- //
- default:
- break;
- }
- }
-
- virtual void
- traverse_composite (semantics::data_member* m, semantics::class_& c)
- {
- if (m != 0)
- traverse_simple (*m); // Do simple value tests.
- else
- names (c); // Don't go into bases.
- }
-
- virtual void
- traverse_pointer (semantics::data_member& m, semantics::class_& c)
- {
- traverse_simple (m); // Do simple value tests.
-
- // Pointer must be deleted before the pointed-to object.
- //
- check_strict (m, c, "object pointer", "pointed-to object");
-
- // Inverse pointer must be deleted before the direct side.
- //
- if (data_member_path* imp = inverse (m))
- check_strict (m, *imp, "inverse object pointer", "direct pointer");
- }
-
- virtual void
- traverse_container (semantics::data_member& m, semantics::type&)
- {
- traverse_simple (m); // Do simple value tests.
-
- if (semantics::class_* c = object_pointer (container_vt (m)))
- {
- // Pointer must be deleted before the pointed-to object.
- //
- check_strict (m, *c, "object pointer", "pointed-to object");
-
- // Inverse pointer must be deleted before the direct side.
- //
- if (data_member_path* imp = inverse (m, "value"))
- check_strict (m, *imp, "inverse object pointer", "direct pointer");
- }
- }
-
- private:
- // Check for harmless things like a data member deleted after the
- // object.
- //
- template <typename D, typename P>
- void
- check_lax (D& dep, P& pre, char const* dname, char const* pname)
- {
- unsigned long long dv (deleted (dep));
- unsigned long long pv (deleted (pre));
-
- if (pv == 0 || // Prerequisite never deleted.
- dv == 0 || // Dependent never deleted.
- dv <= pv) // Dependent deleted before prerequisite.
- return;
-
- location_t dl (dep.template get<location_t> ("deleted-location"));
- location_t pl (pre.template get<location_t> ("deleted-location"));
-
- error (dl) << dname << " is deleted after " << dname << endl;
- info (pl) << pname << " deletion version is specified here" << endl;
-
- valid_ = false;
- }
-
- // Check for harmful things that will result in an invalid database
- // schema, like a pointer pointing to a deleted object.
- //
- template <typename D, typename P>
- void
- check_strict (D& dep, P& pre, char const* dname, char const* pname)
- {
- location_t dl (0), pl (0);
- unsigned long long dv (deleted (dep, &dl));
- unsigned long long pv (deleted (pre, &pl));
-
- if (pv == 0) // Prerequisite never deleted.
- return;
-
- if (dv == 0) // Dependent never deleted.
- {
- location dl (dep.location ());
-
- error (dl) << dname << " is not deleted" << endl;
- info (pl) << pname << " is deleted here" << endl;
-
- valid_ = false;
- }
- else if (pv > dv) // Prerequisite deleted before dependent.
- {
- error (dl) << dname << " is deleted after " << pname << endl;
- info (pl) << pname << " deletion version is specified here" << endl;
-
- valid_ = false;
- }
- }
-
- private:
- bool& valid_;
- };
-
- struct class2: traversal::class_, context
- {
- class2 (bool& valid)
- : valid_ (valid), has_lt_operator_ (0), typedefs_ (true), member_ (valid)
- {
- // Find the has_lt_operator function template.
- //
- tree odb (
- lookup_qualified_name (
- global_namespace, get_identifier ("odb"), false, false));
-
- if (odb != error_mark_node)
- {
- tree compiler (
- lookup_qualified_name (
- odb, get_identifier ("compiler"), false, false));
-
- if (compiler != error_mark_node)
- {
- has_lt_operator_ = lookup_qualified_name (
- compiler, get_identifier ("has_lt_operator"), false, false);
-
- if (has_lt_operator_ != error_mark_node)
- {
-#if BUILDING_GCC_MAJOR >= 8
- has_lt_operator_ = OVL_FIRST (has_lt_operator_);
-#else
- has_lt_operator_ = OVL_CURRENT (has_lt_operator_);
-#endif
- }
- else
- {
- os << unit.file () << ": error: unable to resolve has_lt_operator "
- << "function template inside odb::compiler" << endl;
- has_lt_operator_ = 0;
- }
- }
- else
- os << unit.file () << ": error: unable to resolve compiler "
- << "namespace inside odb" << endl;
- }
- else
- os << unit.file () << ": error: unable to resolve odb namespace"
- << endl;
-
- if (has_lt_operator_ == 0)
- valid_ = false;
-
- *this >> defines_ >> *this;
- *this >> typedefs_ >> *this;
-
- names_member_ >> member_;
- }
-
- virtual void
- traverse (type& c)
- {
- class_kind_type ck (class_kind (c));
-
- if (ck == class_other)
- return;
-
- names (c);
-
- switch (ck)
- {
- case class_object: traverse_object (c); break;
- case class_view: traverse_view (c); break;
- case class_composite: traverse_composite (c); break;
- default: return;
- }
-
- // Check members.
- //
- names (c, names_member_);
-
- // Check version dependencies.
- //
- {
- version_dependencies vd (valid_);
- vd.traverse (c);
- }
- }
-
- virtual void
- traverse_object (type& c)
- {
- bool abst (abstract (c));
- bool poly (polymorphic (c));
-
- // Make sure we have no empty or pointless sections unless we
- // are reuse-abstract or polymorphic.
- //
- if (!poly && !abst)
- {
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- for (user_sections::iterator i (uss.begin ()); i != uss.end (); ++i)
- {
- user_section& s (*i);
-
- // Skip the special version update section (we always treat it
- // as abstract in reuse inheritance).
- //
- if (s.special == user_section::special_version)
- continue;
-
- semantics::data_member& m (*s.member);
- location const& l (m.location ());
-
- if (s.total == 0 && !s.containers)
- {
- error (l) << "empty section" << endl;
-
- if (&m.scope () != &c)
- info (c.location ()) << "as seen in this non-abstract " <<
- "persistent class" << endl;
-
- valid_ = false;
- continue;
- }
-
- // Eager-loaded section with readonly members.
- //
- if (s.load == user_section::load_eager && s.update_empty ())
- {
- error (l) << "eager-loaded section with readonly members is " <<
- "pointless" << endl;
-
- if (&m.scope () != &c)
- info (c.location ()) << "as seen in this non-abstract " <<
- "persistent class" << endl;
-
- valid_ = false;
- }
- }
- }
-
- if (data_member_path* id = id_member (c))
- {
- semantics::type& t (utype (*id));
-
- // If this is a session object, make sure that the id type can
- // be compared.
- //
- if (session (c) && has_lt_operator_ != 0)
- {
- tree args (make_tree_vec (1));
- TREE_VEC_ELT (args, 0) = t.tree_node ();
-
- tree inst (
- instantiate_template (
- has_lt_operator_, args, tf_none));
-
- bool v (inst != error_mark_node);
-
- if (v &&
- DECL_TEMPLATE_INSTANTIATION (inst) &&
- !DECL_TEMPLATE_INSTANTIATED (inst))
- {
- // Instantiate this function template to see if the value type
- // provides operator<. Unfortunately, GCC instantiate_decl()
- // does not provide any control over the diagnostics it issues
- // in case of an error. To work around this, we are going to
- // temporarily redirect diagnostics to /dev/null, which is
- // where asm_out_file points to (see plugin.cxx).
- //
- int ec (errorcount);
- FILE* s (global_dc->printer->buffer->stream);
- global_dc->printer->buffer->stream = asm_out_file;
-
- instantiate_decl (inst, false, false);
-
- global_dc->printer->buffer->stream = s;
- v = (ec == errorcount);
- }
-
- if (!v)
- {
- semantics::data_member& idm (*id->front ());
-
- os << t.file () << ":" << t.line () << ":" << t.column ()
- << ": error: value type that is used as object id in "
- << "persistent class with session support does not define "
- << "the less than (<) comparison" << endl;
-
- os << t.file () << ":" << t.line () << ":" << t.column ()
- << ": info: provide operator< for this value type" << endl;
-
- os << idm.file () << ":" << idm.line () << ":" << idm.column ()
- << ": info: id member is defined here" << endl;
-
- os << c.file () << ":" << c.line () << ":" << c.column ()
- << ": info: persistent class is defined here" << endl;
-
- valid_ = false;
- }
- }
- }
- else
- {
- // Make sure an object without id has no sections.
- //
- user_sections& uss (c.get<user_sections> ("user-sections"));
-
- if (!uss.empty ())
- {
- semantics::data_member& m (*uss.front ().member);
- os << m.file () << ":" << m.line () << ":" << m.column ()
- << ": error: object without id cannot have sections" << endl;
- valid_ = false;
- }
- }
-
- // Allow all the members to be deleted as long as there is no
- // schema for this class.
- //
- column_count_type const& cc (column_count (c));
- size_t cont (has_a (c, test_container));
- size_t dcont (cont - has_a (c, test_container | exclude_deleted));
-
- if ((cc.total == 0 && cont == 0) ||
- (cc.total == cc.deleted && cont == dcont && !(abst || deleted (c))))
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
- valid_ = false;
- }
- }
-
- virtual void
- traverse_view (type& c)
- {
- // We don't check for the column count here since we may want to
- // allow certain kinds of empty views. Instead, this is handled
- // in relational::validation.
-
- // See if any of the associated objects are polymorphic. If so,
- // we need to include polymorphism-specific headers. Also, if the
- // view is loading the object, then we will need the corresponding
- // statements.
- //
- if (c.count ("objects"))
- {
- view_objects& objs (c.get<view_objects> ("objects"));
-
- for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
- {
- if (i->kind == view_object::object)
- {
- if (polymorphic (*i->obj))
- features.polymorphic_object = true;
- else if (i->ptr != 0)
- {
- (id_member (*i->obj) != 0
- ? features.simple_object
- : features.no_id_object) = true;
- }
- }
- }
- }
- }
-
- virtual void
- traverse_composite (type& c)
- {
- // Allow all the members to be deleted.
- //
- column_count_type const& cc (column_count (c));
- size_t cont (has_a (c, test_container));
-
- if (cc.total == 0 && cont == 0)
- {
- os << c.file () << ":" << c.line () << ":" << c.column () << ":"
- << " error: no persistent data members in the class" << endl;
- valid_ = false;
- }
- }
-
- bool& valid_;
- tree has_lt_operator_;
-
- traversal::defines defines_;
- typedefs typedefs_;
-
- data_member2 member_;
- traversal::names names_member_;
- };
-}
-
-void
-validate (options const& ops,
- features& f,
- semantics::unit& u,
- semantics::path const& p,
- unsigned short pass)
-{
- bool valid (true);
- database db (ops.database ()[0]);
-
- // Validate options.
- //
- if (ops.generate_schema_only () &&
- ops.schema_format ()[db].count (schema_format::embedded))
- {
- cerr << "error: --generate-schema-only is only valid when generating " <<
- "schema as a standalone SQL or separate C++ file" << endl;
- valid = false;
- }
-
- // Multi-database support options.
- //
- if (ops.multi_database () == multi_database::dynamic &&
- ops.default_database_specified () &&
- ops.default_database () != database::common)
- {
- cerr << "error: when dynamic multi-database support is used, the " <<
- "default database can only be 'common'" << endl;
- valid = false;
- }
-
- if (db == database::common &&
- ops.multi_database () == multi_database::disabled)
- {
- cerr << "error: 'common' database is only valid with multi-database " <<
- "support enabled" << endl;
- valid = false;
- }
-
- // Changelog options.
- //
- if (ops.changelog_in ().count (db) != ops.changelog_out ().count (db))
- {
- cerr << "error: both --changelog-in and --changelog-out must be " <<
- "specified" << endl;
- valid = false;
- }
-
- if (!valid)
- throw validator_failed ();
-
- unique_ptr<context> ctx (create_context (cerr, u, ops, f, 0));
-
- if (pass == 1)
- {
- traversal::unit unit;
- traversal::defines unit_defines;
- traversal::declares unit_declares;
- typedefs1 unit_typedefs (unit_declares);
- traversal::namespace_ ns;
- value_type vt;
- class1 c (valid);
-
- unit >> unit_defines >> ns;
- unit_defines >> c;
- unit >> unit_declares >> vt;
- unit >> unit_typedefs >> c;
-
- traversal::defines ns_defines;
- traversal::declares ns_declares;
- typedefs1 ns_typedefs (ns_declares);
-
- ns >> ns_defines >> ns;
- ns_defines >> c;
- ns >> ns_declares >> vt;
- ns >> ns_typedefs >> c;
-
- unit.dispatch (u);
- }
- 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 validator_failed ();
-
- switch (db)
- {
- case database::common:
- {
- break;
- }
- case database::mssql:
- case database::mysql:
- case database::oracle:
- case database::pgsql:
- case database::sqlite:
- {
- try
- {
- relational::validate (ops, f, u, p, pass);
- }
- catch (operation_failed const&)
- {
- throw validator_failed ();
- }
-
- break;
- }
- }
-}