summaryrefslogtreecommitdiff
path: root/odb/odb/relational/processor.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/relational/processor.cxx')
-rw-r--r--odb/odb/relational/processor.cxx1564
1 files changed, 1564 insertions, 0 deletions
diff --git a/odb/odb/relational/processor.cxx b/odb/odb/relational/processor.cxx
new file mode 100644
index 0000000..0f60359
--- /dev/null
+++ b/odb/odb/relational/processor.cxx
@@ -0,0 +1,1564 @@
+// file : odb/relational/processor.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <vector>
+#include <algorithm>
+
+#include <odb/diagnostics.hxx>
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+#include <odb/common.hxx>
+
+#include <odb/relational/context.hxx>
+#include <odb/relational/processor.hxx>
+
+using namespace std;
+
+namespace relational
+{
+ namespace
+ {
+ // Indirect (dynamic) context values.
+ //
+ static string
+ id_column_type ()
+ {
+ context& c (context::current ());
+ data_member_path& id (*context::id_member (*c.top_object));
+ return id.back ()->get<string> ("column-id-type");
+ }
+
+ struct data_member: traversal::data_member, context
+ {
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ if (transient (m))
+ return;
+
+ semantics::names* hint;
+ semantics::type& t (utype (m, hint));
+
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (t, whint)))
+ wt = &utype (*wt, whint);
+
+ // Determine the member kind.
+ //
+ enum {simple, composite, container, unknown} kind (unknown);
+
+ // See if this is a composite value type.
+ //
+ if (composite_wrapper (t))
+ kind = composite;
+
+ // If not, see if it is a simple value.
+ //
+ if (kind == unknown)
+ {
+ string type, id_type;
+
+ if (m.count ("id-type"))
+ id_type = m.get<string> ("id-type");
+
+ if (m.count ("type"))
+ {
+ type = m.get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+ }
+
+ if (semantics::class_* c = object_pointer (t))
+ {
+ // An object pointer in view doesn't really have a "column"
+ // so pretend that it has already been handled.
+ //
+ if (view_member (m))
+ kind = simple;
+ else
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // The id type can be a composite value type.
+ //
+ if (composite_wrapper (idt))
+ kind = composite;
+ else
+ {
+ semantics::type* wt;
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest should be identical to the code for the id_type in
+ // the else block.
+ //
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+
+ id_type = type;
+ }
+ }
+ }
+ else
+ {
+ if (id_type.empty () && t.count ("id-type"))
+ id_type = t.get<string> ("id-type");
+
+ if (id_type.empty () && wt != 0 && wt->count ("id-type"))
+ id_type = wt->get<string> ("id-type");
+
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (id_type.empty ())
+ id_type = type;
+
+ if (id_type.empty ())
+ id_type = database_type (t, hint, true);
+
+ if (id_type.empty () && wt != 0)
+ id_type = database_type (*wt, whint, true);
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, false, &null);
+
+ // Use id mapping for discriminators.
+ //
+ if (id (m) || discriminator (m))
+ type = id_type;
+ // Allow NULL if requested by the default mapping.
+ //
+ else if (null && !m.count ("not-null"))
+ m.set ("null", true);
+ }
+
+ if (kind == unknown && !type.empty ())
+ {
+ m.set ("column-type", type);
+ m.set ("column-id-type", id_type);
+
+ // Issue a warning if we are relaxing null-ness.
+ //
+ if (m.count ("null") && t.count ("not-null"))
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " warning: data member declared null while its type is "
+ << "declared not null" << endl;
+ }
+
+ kind = simple;
+ }
+ }
+
+ // If not a simple value, see if this is a container.
+ //
+ if (kind == unknown && context::container (m))
+ {
+ process_container (m, (wt != 0 ? *wt : t));
+ kind = container;
+ }
+
+ // If it is none of the above then we have an error.
+ //
+ if (kind == unknown)
+ {
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << t.fq_name (hint)
+ << "' used in data member '" << m.name () << "' to a "
+ << db.name () << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db type' to specify the database type"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ if (m.count ("polymorphic-ref"))
+ {
+ // Copy the column name from the root's id member, if specified.
+ //
+ {
+ semantics::class_& r (*object_pointer (t));
+ semantics::data_member& id (*id_member (r)->front ());
+
+ if (id.count ("column"))
+ m.set ("column", id.get<table_column> ("column"));
+ }
+
+ m.set ("not-null", true);
+ m.set ("deferrable",
+ sema_rel::deferrable (sema_rel::deferrable::not_deferrable));
+ m.set ("on-delete", sema_rel::foreign_key::cascade);
+ }
+
+ process_index (m);
+ }
+
+ // Convert index/unique specifiers to the index entry in the object.
+ //
+ void
+ process_index (semantics::data_member& m)
+ {
+ bool ip (m.count ("index"));
+ bool up (m.count ("unique"));
+
+ if (ip || up)
+ {
+ using semantics::class_;
+ class_& c (dynamic_cast<class_&> (m.scope ()));
+
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ index in;
+ in.loc = m.get<location_t> (
+ ip ? "index-location" : "unique-location");
+
+ if (up)
+ in.type = "UNIQUE";
+
+ index::member im;
+ im.loc = in.loc;
+ im.name = m.name ();
+ im.path.push_back (&m);
+ in.members.push_back (im);
+
+ // Insert it in the location order.
+ //
+ ins.insert (
+ lower_bound (ins.begin (), ins.end (), in, index_comparator ()),
+ in);
+ }
+ }
+
+ void
+ process_container_value (semantics::type& t,
+ semantics::names* hint,
+ semantics::data_member& m,
+ string const& prefix,
+ bool obj_ptr)
+ {
+ if (composite_wrapper (t))
+ return;
+
+ semantics::names* wh (0);
+ semantics::type* wt (wrapper (t, wh));
+
+ string type;
+ semantics::type& ct (utype (m));
+
+ // Custom mapping can come from these places (listed in the order
+ // of priority): member, container type, value type. To complicate
+ // things a bit, for object references, it can also come from the
+ // member and value type of the id member.
+ //
+ if (m.count (prefix + "-type"))
+ type = m.get<string> (prefix + "-type");
+
+ if (type.empty () && ct.count (prefix + "-type"))
+ type = ct.get<string> (prefix + "-type");
+
+ semantics::class_* c;
+ if (obj_ptr && (c = object_pointer (t)))
+ {
+ // This is an object pointer. The column type is the pointed-to
+ // object id type.
+ //
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ semantics::names* idhint;
+ semantics::type& idt (utype (id, idhint));
+
+ // Nothing to do if this is a composite value type.
+ //
+ if (composite_wrapper (idt))
+ return;
+
+ semantics::type* wt (0);
+ semantics::names* whint (0);
+ if ((wt = wrapper (idt, whint)))
+ wt = &utype (*wt, whint);
+
+ if (type.empty () && id.count ("id-type"))
+ type = id.get<string> ("id-type");
+
+ if (type.empty () && id.count ("type"))
+ type = id.get<string> ("type");
+
+ // The rest of the code is identical to the else block except here
+ // we have to check for "id-type" before checking for "type".
+ //
+
+ if (type.empty () && idt.count ("id-type"))
+ type = idt.get<string> ("id-type");
+
+ if (type.empty () && wt != 0 && wt->count ("id-type"))
+ type = wt->get<string> ("id-type");
+
+ if (type.empty () && idt.count ("type"))
+ type = idt.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ if (type.empty ())
+ type = database_type (idt, idhint, true);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, whint, true);
+ }
+ else
+ {
+ if (type.empty () && t.count ("type"))
+ type = t.get<string> ("type");
+
+ if (type.empty () && wt != 0 && wt->count ("type"))
+ type = wt->get<string> ("type");
+
+ bool null (false);
+ if (type.empty ())
+ type = database_type (t, hint, false, &null);
+
+ if (type.empty () && wt != 0)
+ type = database_type (*wt, wh, false, &null);
+
+ // Allow NULL if requested by the default mapping.
+ //
+ if (null && !m.count (prefix + "-not-null"))
+ m.set (prefix + "-null", true);
+ }
+
+ if (!type.empty ())
+ {
+ m.set (prefix + "-column-type", type);
+ m.set (prefix + "-column-id-type", type);
+ return;
+ }
+
+ // We do not support nested containers so skip that test.
+ //
+
+ // If it is none of the above then we have an error.
+ //
+ string fq_type (t.fq_anonymous () ? "<anonymous>" : t.fq_name ());
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " error: unable to map C++ type '" << fq_type << "' used in "
+ << "data member '" << m.name () << "' to a " << db.name ()
+ << " database type" << endl;
+
+ os << m.file () << ":" << m.line () << ":" << m.column () << ":"
+ << " info: use '#pragma db " << prefix << "_type' to specify the "
+ << "database type" << endl;
+
+ throw operation_failed ();
+ }
+
+ void
+ process_container (semantics::data_member& m, semantics::type& t)
+ {
+ container_kind_type ck (t.get<container_kind_type> ("container-kind"));
+
+ semantics::names* vh (0);
+ semantics::names* ih (0);
+ semantics::names* kh (0);
+
+ semantics::type* vt (&utype (m, vh, "value"));
+ semantics::type* it (ck == ck_ordered ? &utype (m, ih, "index") : 0);
+ semantics::type* kt (ck == ck_map || ck == ck_multimap
+ ? &utype (m, kh, "key")
+ : 0);
+
+ // Process member data.
+ //
+ m.set ("id-column-type", &id_column_type);
+
+ process_container_value (*vt, vh, m, "value", true);
+
+ if (it != 0)
+ process_container_value (*it, ih, m, "index", false);
+
+ if (kt != 0)
+ process_container_value (*kt, kh, m, "key", true);
+ }
+ };
+
+ //
+ //
+ struct view_data_member: traversal::data_member, context
+ {
+ view_data_member (semantics::class_& c)
+ : view_ (c),
+ query_ (c.get<view_query> ("query")),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map"))
+ {
+ }
+
+ struct assoc_member
+ {
+ semantics::data_member* m;
+ view_object* vo;
+ };
+
+ typedef vector<assoc_member> assoc_members;
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ semantics::type& t (utype (m));
+
+ // Object pointers are associated with objects.
+ //
+ if (object_pointer (t))
+ return;
+
+ data_member* src_m (0); // Source member.
+
+ // Resolve member references in column expressions.
+ //
+ if (m.count ("column"))
+ {
+ // Column literal.
+ //
+ if (query_.kind != view_query::condition)
+ {
+ warn (m.get<location_t> ("column-location"))
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ }
+
+ return;
+ }
+ else if (m.count ("column-expr"))
+ {
+ column_expr& e (m.get<column_expr> ("column-expr"));
+
+ if (query_.kind != view_query::condition)
+ {
+ warn (e.loc)
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ return;
+ }
+
+ for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ // This code is quite similar to translate_expression in the
+ // source generator.
+ //
+ try
+ {
+ using semantics::scope;
+ using semantics::class_;
+
+ if (i->kind != column_expr_part::reference)
+ continue;
+
+ lex_.start (i->value);
+
+ string tl;
+ tree tn;
+ cpp_ttype tt (lex_.next (tl, &tn));
+
+ data_member* m (0);
+ view_object* vo (0);
+
+ // Check if this is an alias.
+ //
+ if (tt == CPP_NAME)
+ {
+ view_alias_map::iterator j (amap_.find (tl));
+
+ if (j != amap_.end ())
+ {
+ vo = j->second;
+
+ // Skip '::'.
+ //
+ if (lex_.next (tl, &tn) != CPP_SCOPE)
+ {
+ error (i->loc) << "member name expected after an alias " <<
+ "in db pragma column" << endl;
+ throw operation_failed ();
+ }
+
+ if (lex_.next (tl, &tn) != CPP_NAME)
+ throw lookup::invalid_name ();
+
+ m = &vo->obj->lookup<data_member> (
+ tl, scope::include_hidden);
+
+ tt = lex_.next (tl, &tn);
+ }
+ }
+
+ // If it is not an alias, do the normal lookup.
+ //
+ if (vo == 0)
+ {
+ // Also get the object type. We need to do it so that
+ // we can get the correct (derived) table name (the
+ // member itself can come from a base class).
+ //
+ scope* s;
+ string name;
+ cpp_ttype ptt; // Not used.
+ m = &lookup::resolve_scoped_name<data_member> (
+ lex_, tt, tl, tn, ptt,
+ dynamic_cast<scope&> (*unit.find (i->scope)),
+ name,
+ false,
+ &s);
+
+ view_object_map::iterator j (
+ omap_.find (dynamic_cast<class_*> (s)));
+
+ if (j == omap_.end ())
+ {
+ error (i->loc) << "name '" << name << "' in db pragma " <<
+ "column does not refer to a data member of a " <<
+ "persistent class that is used in this view" << endl;
+ throw operation_failed ();
+ }
+
+ vo = j->second;
+ }
+
+ i->member_path.push_back (m);
+
+ // Figure out the table name/alias for this member.
+ //
+ if (class_* root = polymorphic (*vo->obj))
+ {
+ // If the object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding
+ // table.
+ //
+ class_* c (&static_cast<class_&> (m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (vo->alias.empty ())
+ i->table = t;
+ else
+ i->table = qname (vo->alias + "_" + t.uname ());
+ }
+ else
+ i->table = vo->alias.empty ()
+ ? table_name (*vo->obj)
+ : qname (vo->alias);
+
+ // Finally, resolve nested members if any.
+ //
+ for (; tt == CPP_DOT; tt = lex_.next (tl, &tn))
+ {
+ lex_.next (tl, &tn); // Get CPP_NAME.
+
+ // Check that the outer member is composite and also
+ // unwrap it while at it.
+ //
+ class_* comp (composite_wrapper (utype (*m)));
+ if (comp == 0)
+ {
+ error (i->loc) << "data member '" << m->name () << "' " <<
+ "specified in db pragma column is not composite" << endl;
+ throw operation_failed ();
+ }
+
+ m = &comp->lookup<data_member> (tl, class_::include_hidden);
+ i->member_path.push_back (m);
+ }
+
+ // If the expression is just this reference, then we have
+ // a source member.
+ //
+ if (e.size () == 1)
+ src_m = m;
+ }
+ catch (lookup::invalid_name const&)
+ {
+ error (i->loc) << "invalid name in db pragma column" << endl;
+ throw operation_failed ();
+ }
+ catch (semantics::unresolved const& e)
+ {
+ if (e.type_mismatch)
+ error (i->loc) << "name '" << e.name << "' in db pragma " <<
+ "column does not refer to a data member" << endl;
+ else
+ error (i->loc) << "unable to resolve data member '" <<
+ e.name << "' specified with db pragma column" << endl;
+
+ throw operation_failed ();
+ }
+ catch (semantics::ambiguous const& e)
+ {
+ error (i->loc) << "data member name '" << e.first.name () <<
+ "' specified with db pragma column is ambiguous" << endl;
+
+ info (e.first.named ().location ()) << "could resolve to " <<
+ "this data member" << endl;
+
+ info (e.second.named ().location ()) << "or could resolve " <<
+ "to this data member" << endl;
+
+ throw operation_failed ();
+ }
+ }
+
+ // Check that the source member is not transient or inverse. Also
+ // check that the C++ types are the same (sans cvr-qualification
+ // and wrapping) and issue a warning if they differ. In rare cases
+ // where this is not a mistake, the user can use a phony expression
+ // (e.g., "" + person:name) to disable the warning. Note that in
+ // this case there will be no type pragma copying, which is probably
+ // ok seeing that the C++ types are different.
+ //
+ //
+ if (src_m != 0)
+ {
+ string reason;
+
+ if (transient (*src_m))
+ reason = "transient";
+ else if (inverse (*src_m))
+ reason = "inverse";
+
+ if (!reason.empty ())
+ {
+ error (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column is " << reason << endl;
+ throw operation_failed ();
+ }
+
+ if (!member_resolver::check_types (utype (*src_m), utype (m)))
+ {
+ warn (e.loc)
+ << "object data member '" << src_m->name () << "' specified "
+ << "in db pragma column has a different type compared to the "
+ << "view data member" << endl;
+
+ info (src_m->file (), src_m->line (), src_m->column ())
+ << "object data member is defined here" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "view data member is defined here" << endl;
+ }
+ }
+ }
+ // This member has no column information. If we are generating our
+ // own query, try to find a member with the same (or similar) name
+ // in one of the associated objects.
+ //
+ else if (query_.kind == view_query::condition)
+ {
+ view_objects& objs (view_.get<view_objects> ("objects"));
+
+ assoc_members exact_members, pub_members;
+ member_resolver resolver (exact_members, pub_members, m);
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i->kind == view_object::object)
+ resolver.traverse (*i);
+ }
+
+ assoc_members& members (
+ !exact_members.empty () ? exact_members : pub_members);
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (members.empty ())
+ {
+ error (m.file (), m.line (), m.column ())
+ << "unable to find a corresponding data member for '"
+ << m.name () << "' in any of the associated objects" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to specify the corresponding data "
+ << "member or column name" << endl;
+
+ throw operation_failed ();
+ }
+ else if (members.size () > 1)
+ {
+ error (m.file (), m.line (), m.column ())
+ << "corresponding data member for '" << m.name () << "' is "
+ << "ambiguous" << endl;
+
+ info (m.file (), m.line (), m.column ())
+ << "candidates are:" << endl;
+
+ for (assoc_members::const_iterator i (members.begin ());
+ i != members.end ();
+ ++i)
+ {
+ info (i->m->file (), i->m->line (), i->m->column ())
+ << " '" << i->m->name () << "' in object '"
+ << i->vo->name () << "'" << endl;
+ }
+
+ info (m.file (), m.line (), m.column ())
+ << "use db pragma column to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the column expression for this member.
+ //
+ assoc_member const& am (members.back ());
+
+ column_expr& e (m.set ("column-expr", column_expr ()));
+ e.push_back (column_expr_part ());
+ column_expr_part& ep (e.back ());
+
+ ep.kind = column_expr_part::reference;
+
+
+ // If this object is polymorphic, then figure out which of the
+ // bases this member comes from and use the corresponding table.
+ //
+ using semantics::class_;
+
+ if (class_* root = polymorphic (*am.vo->obj))
+ {
+ class_* c (&static_cast<class_&> (am.m->scope ()));
+
+ // If this member's class is not polymorphic (root uses reuse
+ // inheritance), then use the root table.
+ //
+ if (!polymorphic (*c))
+ c = root;
+
+ // In a polymorphic hierarchy we have several tables and the
+ // provided alias is used as a prefix together with the table
+ // name to form the actual alias.
+ //
+ qname const& t (table_name (*c));
+
+ if (am.vo->alias.empty ())
+ ep.table = t;
+ else
+ ep.table = qname (am.vo->alias + "_" + t.uname ());
+ }
+ else
+ ep.table = am.vo->alias.empty ()
+ ? table_name (*am.vo->obj)
+ : qname (am.vo->alias);
+
+ ep.member_path.push_back (am.m);
+
+ src_m = am.m;
+ }
+
+ // If we have the source member and don't have the type pragma of
+ // our own, but the source member does, then copy the columnt type
+ // over. In case the source member is a pointer, also check the id
+ // member.
+ //
+ if (src_m != 0 && !m.count ("type"))
+ {
+ if (src_m->count ("type"))
+ m.set ("column-type", src_m->get<string> ("column-type"));
+ else if (semantics::class_* c = object_pointer (utype (*src_m)))
+ {
+ semantics::data_member& id (*id_member (*c)->back ());
+
+ if (id.count ("type"))
+ m.set ("column-type", id.get<string> ("column-type"));
+ }
+ }
+
+ // Check the return statements above if you add any extra logic
+ // here.
+ }
+
+ struct member_resolver: traversal::class_
+ {
+ member_resolver (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : member_ (members, pub_members, m)
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (view_object& vo)
+ {
+ member_.vo_ = &vo;
+
+ // First look for an exact match.
+ //
+ {
+ member_.exact_ = true;
+ member_.found_ = false;
+ traverse (*vo.obj);
+ }
+
+ // If we didn't find an exact match, then look for a public
+ // name match.
+ //
+ if (!member_.found_)
+ {
+ member_.exact_ = false;
+ traverse (*vo.obj);
+ }
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ if (!object (c))
+ return; // Ignore transient bases.
+
+ names (c);
+
+ // If we already found a match in one of the derived classes,
+ // don't go into bases to get the standard "hiding" behavior.
+ //
+ if (!member_.found_)
+ inherits (c);
+ }
+
+ public:
+ static bool
+ check_types (semantics::type& ot, semantics::type& vt)
+ {
+ using semantics::type;
+
+ // Require that the types be the same sans the wrapping and
+ // cvr-qualification. If the object member type is a pointer,
+ // use the id type of the pointed-to object.
+ //
+ type* t1;
+
+ if (semantics::class_* c = object_pointer (ot))
+ t1 = &utype (*id_member (*c));
+ else
+ t1 = &ot;
+
+ type* t2 (&vt);
+
+ if (type* wt1 = context::wrapper (*t1))
+ t1 = &utype (*wt1);
+
+ if (type* wt2 = context::wrapper (*t2))
+ t2 = &utype (*wt2);
+
+ if (t1 != t2)
+ return false;
+
+ return true;
+ }
+
+ private:
+ struct data_member: traversal::data_member
+ {
+ data_member (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : members_ (members),
+ pub_members_ (pub_members),
+ name_ (m.name ()),
+ pub_name_ (context::current ().public_name (m)),
+ type_ (utype (m))
+ {
+ }
+
+ virtual void
+ traverse (type& m)
+ {
+ if (exact_)
+ {
+ if (name_ == m.name () && check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ members_.push_back (am);
+ found_ = true;
+ }
+ }
+ else
+ {
+ if (pub_name_ == context::current ().public_name (m) &&
+ check (m))
+ {
+ assoc_member am;
+ am.m = &m;
+ am.vo = vo_;
+ pub_members_.push_back (am);
+ found_ = true;
+ }
+ }
+ }
+
+ bool
+ check (semantics::data_member& m)
+ {
+ // Make sure that the found node can possibly match.
+ //
+ if (context::transient (m) ||
+ context::inverse (m) ||
+ m.count ("polymorphic-ref"))
+ return false;
+
+ return check_types (utype (m), type_);
+ }
+
+ assoc_members& members_;
+ assoc_members& pub_members_;
+
+ string name_;
+ string pub_name_;
+ semantics::type& type_;
+
+ view_object* vo_;
+ bool exact_;
+ bool found_;
+ };
+
+ traversal::names names_;
+ data_member member_;
+ traversal::inherits inherits_;
+ };
+
+ private:
+ semantics::class_& view_;
+ view_query& query_;
+ view_alias_map& amap_;
+ view_object_map& omap_;
+ cxx_string_lexer lex_;
+ };
+
+ struct class_: traversal::class_, context
+ {
+ class_ ()
+ : typedefs_ (true)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+
+ member_names_ >> member_;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type k (class_kind (c));
+
+ if (k == class_other)
+ return;
+
+ names (c); // Process nested classes.
+ names (c, member_names_);
+
+ if (k == class_object)
+ traverse_object (c);
+ else if (k == class_view)
+ traverse_view (c);
+ }
+
+ //
+ // Object.
+ //
+
+ virtual void
+ traverse_object (type& c)
+ {
+ // Remove the bulk pragma if this database doesn't support bulk
+ // operations.
+ //
+ if (c.count ("bulk") && !generate_bulk)
+ c.remove ("bulk");
+
+ // Process indexes. Here we need to do two things: resolve member
+ // names to member paths and assign names to unnamed indexes. We
+ // are also going to handle the special container indexes.
+ //
+ indexes& ins (c.count ("index")
+ ? c.get<indexes> ("index")
+ : c.set ("index", indexes ()));
+
+ for (indexes::iterator i (ins.begin ()); i != ins.end ();)
+ {
+ index& in (*i);
+
+ // This should never happen since a db index pragma without
+ // the member specifier will be treated as a member pragma.
+ //
+ assert (!in.members.empty ());
+
+ // First resolve member names.
+ //
+ index::members_type::iterator j (in.members.begin ());
+ for (; j != in.members.end (); ++j)
+ {
+ index::member& im (*j);
+
+ if (!im.path.empty ())
+ continue; // Already resolved.
+
+ im.path = resolve_data_members (c, im.name, im.loc, lex_);
+
+ if (container (*im.path.back ()))
+ break;
+ }
+
+ // Add the table prefix if this database has global index names.
+ //
+ if (!in.name.empty () && global_index)
+ in.name = table_name_prefix (class_scope (c)) + in.name;
+
+ // Handle container indexes.
+ //
+ if (j != in.members.end ())
+ {
+ // Do some sanity checks.
+ //
+ if (in.members.size () != 1)
+ {
+ error (in.loc) << "multiple data members specified for a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string tl;
+ if (lex_.next (tl) != CPP_DOT || lex_.next (tl) != CPP_NAME ||
+ (tl != "id" && tl != "index"))
+ {
+ error (j->loc) << ".id or .index special member expected in a "
+ << "container index" << endl;
+ throw operation_failed ();
+ }
+
+ string n (tl);
+
+ if (lex_.next (tl) != CPP_EOF)
+ {
+ error (j->loc) << "unexpected text after ." << n << " in "
+ << "db pragma member" << endl;
+ throw operation_failed ();
+ }
+
+ // Move this index to the container member.
+ //
+ j->path.back ()->set (n + "-index", *i);
+ i = ins.erase (i);
+ continue;
+ }
+
+ // Now assign the name if the index is unnamed. We have to
+ // add table name as a prefix here since there is not way
+ // to distinguish between user-assigned and auto-derived
+ // names in the model.
+ //
+ if (in.name.empty ())
+ {
+ // Make sure there is only one member.
+ //
+ if (in.members.size () > 1)
+ {
+ error (in.loc) << "unnamed index with more than one data "
+ << "member" << endl;
+ throw operation_failed ();
+ }
+
+ // Generally, we want the index name to be based on the column
+ // name. This is straightforward for single-column members. In
+ // case of a composite member, we will need to use the column
+ // prefix which is based on the data member name, unless
+ // overridden by the user. In the latter case the prefix can
+ // be empty, in which case we will just fall back on the
+ // member's public name.
+ //
+ string n (column_prefix (in.members.front ().path, true).prefix);
+
+ if (n.empty ())
+ n = public_name_db (*in.members.front ().path.back ());
+ else if (n[n.size () - 1] == '_')
+ n.resize (n.size () - 1); // Remove trailing underscore.
+
+ in.name = index_name (table_name (c), n);
+ }
+
+ ++i;
+ }
+ }
+
+ //
+ // View.
+ //
+
+ struct relationship
+ {
+ semantics::data_member* member;
+ string name;
+ view_object* pointer;
+ view_object* pointee;
+ };
+
+ typedef vector<relationship> relationships;
+
+ virtual void
+ traverse_view (type& c)
+ {
+ bool has_q (c.count ("query"));
+ bool has_o (c.count ("objects"));
+
+ // Determine the kind of query template we've got.
+ //
+ view_query& vq (has_q
+ ? c.get<view_query> ("query")
+ : c.set ("query", view_query ()));
+ if (has_q)
+ {
+ if (!vq.literal.empty ())
+ {
+ string q (upcase (vq.literal));
+
+ //@@ We need to recognize database-specific list of prefixes. For
+ // example, PG has WITH. Alternatively (or in addition) we could
+ // do the same comment trick (e.g., /*SELECT*/ to treat it as a
+ // SELECT-like queiry).
+ //
+ if (q.compare (0, 7, "SELECT ") == 0)
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 ||
+ q.compare (0, 5, "CALL ") == 0 ||
+ q.compare (0, 8, "EXECUTE ") == 0)
+ vq.kind = view_query::complete_execute;
+ //
+ // Hint for databases that use SELECT for stored procedure
+ // calls (e.g., PostgreSQL).
+ //
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.literal = string (vq.literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else if (!vq.expr.empty ())
+ {
+ // If the first token in the expression is a string and
+ // it starts with "SELECT " or is equal to "SELECT" or
+ // one of the stored procedure call keywords, then we
+ // have a complete query.
+ //
+ if (vq.expr.front ().type == CPP_STRING)
+ {
+ string q (upcase (vq.expr.front ().literal));
+
+ if (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT")
+ vq.kind = view_query::complete_select;
+ else if (q.compare (0, 5, "EXEC ") == 0 || q == "EXEC" ||
+ q.compare (0, 5, "CALL ") == 0 || q == "CALL" ||
+ q.compare (0, 8, "EXECUTE ") == 0 || q == "EXECUTE")
+ vq.kind = view_query::complete_execute;
+ else if (q.compare (0, 8, "/*CALL*/") == 0)
+ {
+ vq.expr.front ().literal =
+ string (vq.expr.front ().literal, q[8] == ' ' ? 9 : 8);
+ vq.kind = view_query::complete_execute;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = view_query::condition;
+ }
+ else
+ vq.kind = (vq.distinct || vq.for_update)
+ ? view_query::condition // The query(distinct) case.
+ : view_query::runtime;
+ }
+ else
+ vq.kind = has_o ? view_query::condition : view_query::runtime;
+
+ if ((vq.distinct || vq.for_update) && vq.kind != view_query::condition)
+ {
+ error (vq.loc)
+ << "result modifier specified for "
+ << (vq.kind == view_query::runtime ? "runtime" : "native")
+ << " query" << endl;
+
+ throw operation_failed ();
+ }
+
+ // We cannot have an incomplete query if there are not objects
+ // to derive the rest from.
+ //
+ if (vq.kind == view_query::condition && !has_o)
+ {
+ error (c.file (), c.line (), c.column ())
+ << "view '" << class_fq_name (c) << "' has an incomplete query "
+ << "template and no associated objects" << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "use db pragma query to provide a complete query template"
+ << endl;
+
+ info (c.file (), c.line (), c.column ())
+ << "or use db pragma object to associate one or more objects "
+ << "with the view"
+ << endl;
+
+ throw operation_failed ();
+ }
+
+ // Process join conditions.
+ //
+ if (has_o)
+ {
+ view_objects& objs (c.get<view_objects> ("objects"));
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ {
+ if (i == objs.begin () && i->join != view_object::left)
+ {
+ error (i->loc)
+ << "no join type can be specified for the first associated "
+ << (i->kind == view_object::object ? "object" : "table")
+ << endl;
+ throw operation_failed ();
+ }
+
+ if (i->kind != view_object::object)
+ {
+ // Make sure we have join conditions for tables unless it
+ // is the first entry.
+ //
+ if (i != objs.begin () && i->cond.empty ())
+ {
+ error (i->loc)
+ << "missing join condition in db pragma table" << endl;
+
+ throw operation_failed ();
+ }
+
+ continue;
+ }
+
+ // If we have to generate the query and there was no JOIN
+ // condition specified by the user, try to come up with one
+ // automatically based on object relationships. CROSS JOIN
+ // has no condition.
+ //
+ if (vq.kind == view_query::condition &&
+ i->cond.empty () &&
+ i != objs.begin () &&
+ i->join != view_object::cross)
+ {
+ relationships rs;
+
+ // Check objects specified prior to this one for any
+ // relationships. We don't examine objects that were
+ // specified after this one because that would require
+ // rearranging the JOIN order.
+ //
+ for (view_objects::iterator j (objs.begin ()); j != i; ++j)
+ {
+ if (j->kind != view_object::object)
+ continue; // Skip tables.
+
+ // First see if any of the objects that were specified
+ // prior to this object point to it.
+ //
+ {
+ relationship_resolver r (rs, *i, true);
+ r.traverse (*j);
+ }
+
+ // Now see if this object points to any of the objects
+ // specified prior to it.
+ //
+ {
+ relationship_resolver r (rs, *j, false);
+ r.traverse (*i);
+ }
+ }
+
+ // Issue diagnostics if we didn't find any or found more
+ // than one.
+ //
+ if (rs.empty ())
+ {
+ error (i->loc)
+ << "unable to find an object relationship involving "
+ << "object '" << i->name () << "' and any of the previously "
+ << "associated objects" << endl;
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to specify a custom join condition" << endl;
+
+ throw operation_failed ();
+ }
+ else if (rs.size () > 1)
+ {
+ error (i->loc)
+ << "object relationship for object '" << i->name () << "' "
+ << "is ambiguous" << endl;
+
+ info (i->loc)
+ << "candidates are:" << endl;
+
+ for (relationships::const_iterator j (rs.begin ());
+ j != rs.end ();
+ ++j)
+ {
+ semantics::data_member& m (*j->member);
+
+ info (m.file (), m.line (), m.column ())
+ << " '" << j->name << "' "
+ << "in object '" << j->pointer->name () << "' "
+ << "pointing to '" << j->pointee->name () << "'"
+ << endl;
+ }
+
+ info (i->loc)
+ << "use the join condition clause in db pragma object "
+ << "to resolve this ambiguity" << endl;
+
+ throw operation_failed ();
+ }
+
+ // Synthesize the condition.
+ //
+ relationship const& r (rs.back ());
+
+ string name (r.pointer->alias.empty ()
+ ? class_fq_name (*r.pointer->obj)
+ : r.pointer->alias);
+ name += "::";
+ name += r.name;
+
+ lex_.start (name);
+
+ string t;
+ for (cpp_ttype tt (lex_.next (t));
+ tt != CPP_EOF;
+ tt = lex_.next (t))
+ {
+ i->cond.push_back (cxx_token (lex_.location (), tt, t));
+ }
+ }
+ }
+ }
+
+ // Handle forced versioning. When versioning is forced, ignore
+ // it for native views.
+ //
+ if (force_versioned && vq.kind == view_query::condition)
+ c.set ("versioned", true);
+
+ // Handle data members.
+ //
+ {
+ view_data_member t (c);
+ traversal::names n (t);
+ names (c, n);
+ }
+ }
+
+ struct relationship_resolver: object_members_base
+ {
+ relationship_resolver (relationships& rs,
+ view_object& pointee,
+ bool forward)
+ // Look in polymorphic bases only for previously-associated
+ // objects since backward pointers from bases will result in
+ // the pathological case (we will have to join the base table
+ // first, which means we will get both bases and derived objects
+ // instead of just derived).
+ //
+ : object_members_base (false, false, true, forward),
+ relationships_ (rs),
+ // Ignore self-references if we are looking for backward
+ // pointers since they were already added to the list in
+ // the previous pass.
+ //
+ self_pointer_ (forward),
+ pointer_ (0),
+ pointee_ (pointee)
+ {
+ }
+
+ void
+ traverse (view_object& pointer)
+ {
+ pointer_ = &pointer;
+ object_members_base::traverse (*pointer.obj);
+ }
+
+ using object_members_base::traverse; // Unhide.
+
+ virtual void
+ traverse_pointer (semantics::data_member& m, semantics::class_& c)
+ {
+ // Ignore polymorphic id references.
+ //
+ if (m.count ("polymorphic-ref"))
+ return;
+
+ // Ignore inverse sides of the same relationship to avoid
+ // phony conflicts caused by the direct side that will end
+ // up in the relationship list as well. Unless the inverse
+ // member is in the polymorphic base in which case we will
+ // miss it since we don't examine inside poly bases on the
+ // backwards scan (see above).
+ //
+ if (data_member_path* imp = inverse (m))
+ {
+ if (&imp->front ()->scope () == &c) // Direct member.
+ return;
+ }
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == &c)
+ return;
+
+ if (pointee_.obj == &c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+
+ virtual void
+ traverse_container (semantics::data_member& m, semantics::type&)
+ {
+ if (semantics::class_* c =
+ object_pointer (context::container_vt (m)))
+ {
+ if (inverse (m, "value"))
+ return;
+
+ // Ignore self-pointers if requested.
+ //
+ if (!self_pointer_ && pointer_->obj == c)
+ return;
+
+ if (pointee_.obj == c)
+ {
+ relationships_.push_back (relationship ());
+ relationships_.back ().member = &m;
+ relationships_.back ().name = member_prefix_ + m.name ();
+ relationships_.back ().pointer = pointer_;
+ relationships_.back ().pointee = &pointee_;
+ }
+ }
+ }
+
+ private:
+ relationships& relationships_;
+ bool self_pointer_;
+ view_object* pointer_;
+ view_object& pointee_;
+ };
+
+ private:
+ cxx_string_lexer lex_;
+
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ data_member member_;
+ traversal::names member_names_;
+ };
+ }
+
+ void
+ process ()
+ {
+ context ctx;
+
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c;
+
+ 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 (ctx.unit);
+ }
+}