summaryrefslogtreecommitdiff
path: root/odb/relational/processor.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/relational/processor.cxx')
-rw-r--r--odb/relational/processor.cxx1562
1 files changed, 0 insertions, 1562 deletions
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
deleted file mode 100644
index aac8d79..0000000
--- a/odb/relational/processor.cxx
+++ /dev/null
@@ -1,1562 +0,0 @@
-// 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);
- }
-
- 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);
- }
-}