summaryrefslogtreecommitdiff
path: root/odb/odb/context.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/context.hxx')
-rw-r--r--odb/odb/context.hxx1941
1 files changed, 1941 insertions, 0 deletions
diff --git a/odb/odb/context.hxx b/odb/odb/context.hxx
new file mode 100644
index 0000000..ec4505b
--- /dev/null
+++ b/odb/odb/context.hxx
@@ -0,0 +1,1941 @@
+// file : odb/context.hxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#ifndef ODB_CONTEXT_HXX
+#define ODB_CONTEXT_HXX
+
+#include <odb/gcc-fwd.hxx>
+
+#include <map>
+#include <set>
+#include <list>
+#include <stack>
+#include <vector>
+#include <string>
+#include <memory> // std::unique_ptr
+#include <ostream>
+#include <cstddef> // std::size_t
+#include <iostream>
+
+#include <libcutl/re.hxx>
+#include <libcutl/shared-ptr.hxx>
+
+#include <odb/options.hxx>
+#include <odb/features.hxx>
+#include <odb/location.hxx>
+#include <odb/cxx-token.hxx>
+#include <odb/semantics.hxx>
+#include <odb/semantics/relational/name.hxx>
+#include <odb/semantics/relational/model.hxx>
+#include <odb/traversal.hxx>
+
+using std::endl;
+using std::cerr;
+
+// Regex.
+//
+using cutl::re::regex;
+using cutl::re::regexsub;
+typedef cutl::re::format regex_format;
+
+typedef std::vector<regexsub> regex_mapping;
+
+// Forward-declarations.
+//
+class cxx_string_lexer;
+
+// Generic exception thrown to indicate a failure when diagnostics
+// has already been issued (to stderr).
+//
+class operation_failed {};
+
+// Keep this enum synchronized with the one in libodb/odb/pointer-traits.hxx.
+//
+enum pointer_kind
+{
+ pk_raw,
+ pk_unique,
+ pk_shared,
+ pk_weak
+};
+
+// Keep this enum synchronized with the one in libodb/odb/container-traits.hxx.
+//
+enum container_kind
+{
+ ck_ordered,
+ ck_set,
+ ck_multiset,
+ ck_map,
+ ck_multimap
+};
+
+// The same as class_kind in libodb/odb/traits.hxx.
+//
+enum class_kind
+{
+ class_object,
+ class_view,
+ class_composite,
+ class_other
+};
+
+// Data member path.
+//
+// If it is a direct member of an object, then we will have just
+// one member. However, if this is a member inside a composite
+// value, then we will have a "path" constructed out of members
+// that lead all the way from the object member to the innermost
+// composite value member.
+//
+struct data_member_path: std::vector<semantics::data_member*>
+{
+ data_member_path () {}
+
+ explicit
+ data_member_path (semantics::data_member& m) {push_back (&m);}
+
+ // Return true if this is a sub-path of (or equal to) the
+ // specified path.
+ //
+ bool
+ sub (const data_member_path& p) const
+ {
+ size_t n (p.size ());
+
+ if (n > size ())
+ return false;
+
+ for (size_t i (0); i != n; ++i)
+ if ((*this)[i] != p[i])
+ return false;
+
+ return true;
+ }
+};
+
+// Class inheritance chain, from the most derived to base.
+//
+typedef std::vector<semantics::class_*> class_inheritance_chain;
+
+// A list of inheritance chains for a data member in an object.
+// The first entry in this list would correspond to the object.
+// All subsequent entries, if any, correspond to composite
+// values.
+//
+typedef std::vector<class_inheritance_chain> data_member_scope;
+
+//
+// Semantic graph context types.
+//
+
+// Custom C++ type mapping.
+//
+struct custom_cxx_type
+{
+ custom_cxx_type (): type_node (0), as_node (0) {}
+
+ std::string
+ translate_to (std::string const& v) const {return translate (v, to);}
+
+ std::string
+ translate_from (std::string const& v) const {return translate (v, from);}
+
+ tree type_node;
+ std::string type_name;
+ semantics::type* type;
+ semantics::names* type_hint;
+
+ tree as_node;
+ std::string as_name;
+ semantics::type* as;
+ semantics::names* as_hint;
+
+ cxx_tokens to;
+ bool to_move; // Single (?), so can move.
+
+ cxx_tokens from;
+ bool from_move; // Single (?), so can move.
+
+ location_t loc;
+ tree scope; // Scope for which this mapping is defined.
+
+private:
+ static std::string
+ translate (std::string const&, const cxx_tokens&);
+};
+
+typedef std::vector<custom_cxx_type> custom_cxx_types;
+typedef std::map<semantics::type*, custom_cxx_type*> custom_cxx_type_map;
+
+
+// Object or view pointer.
+//
+struct class_pointer
+{
+ std::string name;
+ tree scope;
+ location_t loc;
+};
+
+//
+//
+struct default_value
+{
+ enum kind_type
+ {
+ reset, // Default value reset.
+ null,
+ boolean, // Literal contains value (true or false).
+ integer, // Integer number. Literal contains sign.
+ floating, // Floating-point number.
+ string, // Literal contains value.
+ enumerator // Literal is the name, enum_value is the tree node.
+ };
+
+ kind_type kind;
+ std::string literal;
+
+ union
+ {
+ tree enum_value;
+ unsigned long long int_value;
+ double float_value;
+ };
+};
+
+// Database potentially-qualified and unqualifed names.
+//
+using semantics::relational::qname;
+using semantics::relational::uname;
+
+// Object or table associated with the view.
+//
+struct view_object
+{
+ // Return a diagnostic name for this association. It is either the
+ // alias, unqualified object name, or string representation of the
+ // table name.
+ //
+ std::string
+ name () const;
+
+ enum kind_type { object, table };
+ enum join_type { left, right, full, inner, cross };
+
+ kind_type kind;
+ join_type join;
+ tree obj_node; // Tree node if kind is object.
+ std::string obj_name; // Name as specified in the pragma if kind is object.
+ qname tbl_name; // Table name if kind is table.
+ std::string alias;
+ tree scope;
+ location_t loc;
+ semantics::class_* obj;
+ semantics::data_member* ptr; // Corresponding object pointer, if any.
+
+ cxx_tokens cond; // Join condition tokens.
+};
+
+typedef std::vector<view_object> view_objects;
+
+// The view_alias_map does not contain entries for tables.
+//
+typedef std::map<std::string, view_object*> view_alias_map;
+typedef std::map<semantics::class_*, view_object*> view_object_map;
+
+// Collection of relationships via which the objects are joined.
+// We need this information to figure out which alias/table
+// names to use for columns corresponding to inverse object
+// pointers inside objects that this view may be loading.
+//
+// The first object is the pointer (i.e., the one containing
+// this data member) while the other is the pointee. In other
+// words, first -> second. We always store the direct (i.e.,
+// non-inverse) side of the relationship. Note also that there
+// could be multiple objects joined using the same relationship.
+//
+typedef
+std::multimap<data_member_path, std::pair<view_object*, view_object*> >
+view_relationship_map;
+
+//
+//
+struct view_query
+{
+ view_query (): distinct (false), for_update (false) {}
+
+ enum kind_type
+ {
+ runtime,
+ complete_select, // SELECT query.
+ complete_execute, // Stored procedure call.
+ condition
+ };
+
+ kind_type kind;
+ std::string literal;
+ cxx_tokens expr;
+ tree scope;
+ location_t loc;
+
+ // Result modifiers (only for condition).
+ //
+ bool distinct; // SELECT DISTINCT
+ bool for_update; // SELECT FOR UPDATE
+};
+
+//
+//
+struct table_column
+{
+ qname table;
+ std::string column;
+ bool expr; // True if column is an expression, and therefore should not
+ // be quoted.
+
+ table_column () {}
+
+ explicit
+ table_column (const std::string& c): column (c), expr (false) {}
+};
+
+//
+//
+struct column_expr_part
+{
+ enum kind_type
+ {
+ literal,
+ reference
+ };
+
+ kind_type kind;
+ std::string value;
+ qname table; // Table name/alias for references.
+ data_member_path member_path; // Path to member for references.
+
+ // Scope and location of this pragma. Used to resolve the member name.
+ //
+ tree scope;
+ location_t loc;
+};
+
+struct column_expr: std::vector<column_expr_part>
+{
+ location_t loc;
+};
+
+//
+//
+struct member_access
+{
+ member_access (const location& l, const char* k, bool s)
+ : loc (l), kind (k), synthesized (s), by_value (false) {}
+
+ // Return true of we have the (?) placeholder.
+ //
+ bool
+ placeholder () const;
+
+ // Return true if this is a synthesized expression that goes
+ // directly for the member.
+ //
+ bool
+ direct () const
+ {
+ return synthesized && expr.size () == 3; // this.member
+ }
+
+ bool
+ empty () const
+ {
+ return expr.empty ();
+ }
+
+ // Issues diagnostics and throws operation_failed if expression is
+ // empty.
+ //
+ std::string
+ translate (std::string const& obj,
+ std::string const& val = std::string (),
+ std::string const& db = std::string ()) const;
+
+ location loc;
+ const char* kind; // accessor/modifier; used for diagnostics.
+ bool synthesized; // If true, then this is a synthesized expression.
+ cxx_tokens expr;
+ bool by_value; // True if accessor returns by value. False doesn't
+ // necessarily mean that it is by reference.
+};
+
+//
+//
+struct model_version
+{
+ unsigned long long base;
+ unsigned long long current;
+ bool open;
+};
+
+// Sections.
+//
+struct object_section
+{
+ virtual bool
+ compare (object_section const&) const = 0;
+
+ virtual bool
+ separate_load () const = 0;
+
+ virtual bool
+ separate_update () const = 0;
+};
+
+inline bool
+operator== (object_section const& x, object_section const& y)
+{
+ return x.compare (y);
+}
+
+inline bool
+operator!= (object_section const& x, object_section const& y)
+{
+ return !x.compare (y);
+}
+
+// Main section.
+//
+struct main_section_type: object_section
+{
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return false;}
+
+ virtual bool
+ separate_update () const {return false;}
+};
+
+inline bool
+operator== (main_section_type const&, main_section_type const&)
+{
+ return true; // There is only one main section.
+}
+
+extern main_section_type main_section;
+
+// User-defined section.
+//
+struct user_section: object_section
+{
+ enum load_type
+ {
+ load_eager,
+ load_lazy
+ };
+
+ enum update_type
+ {
+ update_always,
+ update_change,
+ update_manual
+ };
+
+ enum special_type
+ {
+ special_ordinary,
+ special_version // Fake section for optimistic version update.
+ };
+
+ user_section (semantics::data_member& m,
+ semantics::class_& o,
+ std::size_t i,
+ load_type l,
+ update_type u,
+ special_type s = special_ordinary)
+ : member (&m), object (&o), base (0), index (i),
+ load (l), update (u), special (s),
+ total (0), inverse (0), readonly (0), versioned (false),
+ containers (false), readwrite_containers (false),
+ versioned_containers (false), readwrite_versioned_containers (false) {}
+
+ virtual bool
+ compare (object_section const& s) const;
+
+ virtual bool
+ separate_load () const {return load != load_eager;}
+
+ virtual bool
+ separate_update () const
+ {
+ // A separately-loaded section is always separately-updated since
+ // it might not be loaded when update is requested.
+ //
+ return separate_load () || update != update_always;
+ }
+
+ bool
+ load_empty () const;
+
+ bool
+ update_empty () const;
+
+ bool
+ empty () const
+ {
+ return load_empty () && update_empty ();
+ }
+
+ // A section is optimistic if the object that contains it is optimistic.
+ // For polymorphic hierarchies, only sections contained in the root are
+ // considered optimistic.
+ //
+ bool
+ optimistic () const;
+
+ semantics::data_member* member; // Data member of this section.
+ semantics::class_* object; // Object containing this section.
+ user_section* base; // Base of this section.
+ std::size_t index; // Index of this sections.
+
+ load_type load;
+ update_type update;
+ special_type special;
+
+ // Column counts.
+ //
+ std::size_t total;
+ std::size_t inverse;
+ std::size_t readonly;
+
+ bool versioned;
+
+ bool containers;
+ bool readwrite_containers;
+
+ bool versioned_containers;
+ bool readwrite_versioned_containers;
+
+ // Total counts across overrides.
+ //
+ std::size_t
+ total_total () const
+ {
+ user_section* b (total_base ());
+ return total + (b == 0 ? 0 : b->total_total ());
+ }
+
+ std::size_t
+ total_inverse () const
+ {
+ user_section* b (total_base ());
+ return inverse + (b == 0 ? 0 : b->total_inverse ());
+ }
+
+ std::size_t
+ total_readonly () const
+ {
+ user_section* b (total_base ());
+ return readonly + (b == 0 ? 0 : b->total_readonly ());
+ }
+
+ bool
+ total_containers ()
+ {
+ user_section* b (total_base ());
+ return containers || (b != 0 && b->total_containers ());
+ }
+
+ bool
+ total_readwrite_containers ()
+ {
+ user_section* b (total_base ());
+ return readwrite_containers ||
+ (b != 0 && b->total_readwrite_containers ());
+ }
+
+private:
+ user_section*
+ total_base () const;
+};
+
+inline bool
+operator== (user_section const& x, user_section const& y)
+{
+ return x.member == y.member;
+}
+
+// Using list for pointer for element stability (see user_section::base).
+//
+struct user_sections: std::list<user_section>
+{
+ // Count sections that have something to load.
+ //
+ static unsigned short const count_load = 0x01;
+
+ // Count sections that are non-eager but have nothing to load.
+ //
+ static unsigned short const count_load_empty = 0x02;
+
+ // Count sections that have something to update.
+ //
+ static unsigned short const count_update = 0x04;
+
+ // Count sections that have nothing to update.
+ //
+ static unsigned short const count_update_empty = 0x08;
+
+ // Count sections that are optimistic.
+ //
+ static unsigned short const count_optimistic = 0x10;
+
+ // Modifiers:
+ //
+
+ // Don't exclude fake optimistic version update section from the count.
+ //
+ static unsigned short const count_special_version = 0x20;
+
+ // Only count versioned sections.
+ //
+ static unsigned short const count_versioned_only = 0x40;
+
+
+ // Count all sections, but excluding special.
+ //
+ static unsigned short const count_all = count_update |
+ count_update_empty;
+
+ static unsigned short const count_new = 0x1000;
+ static unsigned short const count_override = 0x2000;
+ static unsigned short const count_total = 0x4000;
+
+ std::size_t
+ count (unsigned short flags) const;
+
+ user_sections (semantics::class_& o): object (&o) {};
+ semantics::class_* object;
+};
+
+// Context.
+//
+class context
+{
+public:
+ typedef std::size_t size_t;
+ typedef std::string string;
+ typedef std::vector<string> strings;
+ typedef std::ostream ostream;
+
+ typedef ::options options_type;
+
+ static string
+ upcase (string const&);
+
+public:
+ // Return cvr-unqualified base of the type, or type itself, if it is
+ // not qualified.
+ //
+ static semantics::type&
+ utype (semantics::type&);
+
+ // The same as above, but also returns the name hint for the unqualified
+ // type. If the original type is already unqualified, then the hint
+ // argument is not modified.
+ //
+ static semantics::type&
+ utype (semantics::type&, semantics::names*& hint);
+
+ // The same for a member's type but also do custom C++ type translation.
+ //
+ static semantics::type&
+ utype (semantics::data_member& m, const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, string (), translation);
+ }
+
+ static semantics::type&
+ utype (semantics::data_member& m,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ semantics::names* hint (0);
+ return utype (m, hint, key_prefix, translation);
+ }
+
+ // In addition to the unqualified type, this version also returns the
+ // name hint for this type. If the member type is already unqualified,
+ // then the hint is from the belongs edge. Otherwise, it is from the
+ // qualifies edge.
+ //
+ static semantics::type&
+ utype (semantics::data_member&,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0);
+
+ static semantics::type&
+ utype (const data_member_path& mp, const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ string const& key_prefix,
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), key_prefix, translation);
+ }
+
+ static semantics::type&
+ utype (const data_member_path& mp,
+ semantics::names*& hint,
+ string const& key_prefix = string (),
+ const custom_cxx_type** translation = 0)
+ {
+ return utype (*mp.back (), hint, key_prefix, translation);
+ }
+
+ // For arrays this function returns true if the (innermost) element
+ // type is const.
+ //
+ static bool
+ const_type (semantics::type&);
+
+ static bool
+ const_member (semantics::data_member& m) {return const_type (m.type ());}
+
+ // Form a reference type for a not mapped, actual member type. If
+ // make_const is true, then add top-level const qualifier, unless
+ // it is already there. If it is false, then strip it if it is
+ // already there. If var is not empty, then embed the variable
+ // name into the type (e.g., char (*v)[3]). If decay_array is
+ // false then don't decay the (top-level) array to a pointer.
+ //
+ static string
+ member_ref_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true)
+ {
+ return type_ref_type (
+ m.type (), m.belongs ().hint (), make_const, var, decay_array);
+ }
+
+ static string
+ type_ref_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "",
+ bool decay_array = true);
+
+ // Form a value type for a not mapped, actual member type. If make_const
+ // is true, then add top-level const qualifier, unless it is already
+ // there. If it is false, then strip it if it is already there. If var is
+ // not empty, then embed the variable name into the type (e.g., char v[3]).
+ //
+ static string
+ member_val_type (semantics::data_member& m,
+ bool make_const,
+ string const& var = "")
+ {
+ return type_val_type (m.type (), m.belongs ().hint (), make_const, var);
+ }
+
+ static string
+ type_val_type (semantics::type&,
+ semantics::names* hint,
+ bool make_const,
+ string const& var = "");
+
+ // Member access helpers. Database can be empty. If type is not empty,
+ // then it should be the non-cvr type of the member (e.g., id_type).
+ //
+ void
+ set_member (semantics::data_member& m,
+ const std::string& obj,
+ const std::string& val,
+ const std::string& db,
+ const std::string& type = "");
+
+ void
+ inc_member (semantics::data_member& m,
+ const std::string& sobj, // Set expression object.
+ const std::string& gobj, // Get expression object.
+ const std::string& type = "");
+
+public:
+ // Resolve data member name in the form "a.b.c" to the data member path,
+ // issuing diagnostics and throwing operation_filed in case of an error.
+ // This function stops if it encounters a container leaving lex usable
+ // to continue parsing.
+ //
+ void
+ resolve_data_members (data_member_path& append,
+ semantics::class_& scope,
+ const std::string& name,
+ const location&,
+ cxx_string_lexer&);
+
+ data_member_path
+ resolve_data_members (semantics::class_& scope,
+ const std::string& name,
+ const location& l,
+ cxx_string_lexer& lex)
+ {
+ data_member_path r;
+ resolve_data_members (r, scope, name, l, lex);
+ return r;
+ }
+
+ // Predicates.
+ //
+public:
+ static bool
+ object (semantics::type& t)
+ {
+ return t.count ("object");
+ }
+
+ static bool
+ view (semantics::type& t)
+ {
+ return t.count ("view");
+ }
+
+ // Direct member of a view.
+ //
+ static bool
+ view_member (semantics::data_member& m)
+ {
+ return view (dynamic_cast<semantics::class_&> (m.scope ()));
+ }
+
+ // Check whether the type is a wrapper. Return the wrapped type if
+ // it is a wrapper and NULL otherwise. Note that the returned type
+ // may be cvr-qualified.
+ //
+ static semantics::type*
+ wrapper (semantics::type& t)
+ {
+ return t.count ("wrapper") && t.get<bool> ("wrapper")
+ ? t.get<semantics::type*> ("wrapper-type")
+ : 0;
+ }
+
+ static semantics::type*
+ wrapper (semantics::type& t, semantics::names*& hint)
+ {
+ if (t.count ("wrapper") && t.get<bool> ("wrapper"))
+ {
+ hint = t.get<semantics::names*> ("wrapper-hint");
+ return t.get<semantics::type*> ("wrapper-type");
+ }
+ else
+ return 0;
+ }
+
+ // Composite value type is a class type that was explicitly marked
+ // as value type and there was no database type mapping provided for
+ // it by the user (specifying the database type makes the value type
+ // simple).
+ //
+ static bool
+ composite (semantics::class_& c)
+ {
+ if (c.count ("composite-value"))
+ return c.get<bool> ("composite-value");
+ else
+ return composite_ (c);
+ }
+
+ // Return the class object if this type is a composite value type
+ // and NULL otherwise.
+ //
+ static semantics::class_*
+ composite (semantics::type& t)
+ {
+ semantics::class_* c (dynamic_cast<semantics::class_*> (&t));
+ return c != 0 && composite (*c) ? c : 0;
+ }
+
+ // As above but also "sees through" wrappers.
+ //
+ static semantics::class_*
+ composite_wrapper (semantics::type& t)
+ {
+ if (semantics::class_* c = composite (t))
+ return c;
+ else if (semantics::type* wt = wrapper (t))
+ return composite (utype (*wt));
+ else
+ return 0;
+ }
+
+ // Check if a data member is a container. "Sees through" wrappers and
+ // returns the actual container type or NULL if not a container.
+ //
+ // We require data member as input instead of the type because the
+ // same type (e.g., vector<char>) can be used for both container
+ // and simple value members.
+ //
+ static semantics::type*
+ container (semantics::data_member& m)
+ {
+ // The same type can be used as both a container and a simple value.
+ //
+ if (m.count ("simple"))
+ return 0;
+
+ semantics::type* t (&utype (m));
+
+ if (semantics::type* wt = wrapper (*t))
+ t = &utype (*wt);
+
+ return t->count ("container-kind") ? t : 0;
+ }
+
+ static semantics::class_*
+ object_pointer (semantics::type& t)
+ {
+ return t.get<semantics::class_*> ("element-type", 0);
+ }
+
+ // If this data member is or is part of an object pointer, then
+ // return the member that is the pointer. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ object_pointer (data_member_path const&);
+
+ static semantics::class_*
+ points_to (semantics::data_member& m)
+ {
+ return m.get<semantics::class_*> ("points-to", 0);
+ }
+
+ static bool
+ abstract (semantics::class_& c)
+ {
+ // If a class is abstract in the C++ sense then it is also abstract in
+ // the database sense.
+ //
+ return c.abstract () || c.count ("abstract");
+ }
+
+ static bool
+ session (semantics::class_& c)
+ {
+ return c.get<bool> ("session");
+ }
+
+ static bool
+ transient (semantics::data_member& m)
+ {
+ return m.count ("transient");
+ }
+
+ // Return the deletion version or 0 if not soft-deleted.
+ //
+ static unsigned long long
+ deleted (semantics::class_& c, location_t* l = 0)
+ {
+ unsigned long long v (c.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = c.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (semantics::data_member& m, location_t* l = 0)
+ {
+ unsigned long long v (m.get<unsigned long long> ("deleted", 0));
+
+ if (v != 0 && l != 0)
+ *l = m.get<location_t> ("deleted-location");
+
+ return v;
+ }
+
+ static unsigned long long
+ deleted (data_member_path const& mp, location_t* l = 0)
+ {
+ unsigned long long r (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+
+ if (l != 0)
+ *l = (*i)->get<location_t> ("deleted-location");
+ }
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ deleted_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the earliest version since this member was deleted.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("deleted", 0));
+ if (v != 0 && (r == 0 || v < r))
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ // Return the addition version or 0 if not soft-added.
+ //
+ static unsigned long long
+ added (semantics::class_& c) // Used for composite only.
+ {
+ return c.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (semantics::data_member& m)
+ {
+ return m.get<unsigned long long> ("added", 0);
+ }
+
+ static unsigned long long
+ added (data_member_path const& mp)
+ {
+ unsigned long long r (0);
+
+ // Find the latest version since this member was added.
+ //
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ r = v;
+ }
+
+ return r;
+ }
+
+ static semantics::data_member*
+ added_member (data_member_path const& mp)
+ {
+ semantics::data_member* m (0);
+
+ // Find the latest version since this member was added.
+ //
+ unsigned long long r (0);
+ for (data_member_path::const_reverse_iterator i (mp.rbegin ());
+ i != mp.rend (); ++i)
+ {
+ unsigned long long v ((*i)->get<unsigned long long> ("added", 0));
+ if (v != 0 && v > r)
+ {
+ r = v;
+ m = *i;
+ }
+ }
+
+ return m;
+ }
+
+ static bool
+ id (semantics::data_member& m)
+ {
+ return m.count ("id");
+ }
+
+ // If this data member is or is part of an id member, then return
+ // the member that is marked as the id. Otherwise, return 0.
+ //
+ static semantics::data_member*
+ id (data_member_path const&);
+
+ static bool
+ auto_ (semantics::data_member& m)
+ {
+ return id (m) && m.count ("auto");
+ }
+
+ // Must be a path returned by id(). In other words, it assumes
+ // the path is to the id member.
+ //
+ static bool
+ auto_ (data_member_path& mp)
+ {
+ return mp.front ()->count ("auto");
+ }
+
+ // The member scope is used to override readonly status when a readonly
+ // class (object or composite value) inherits from a readwrite base.
+ //
+ static bool
+ readonly (data_member_path const&, data_member_scope const&);
+
+ static bool
+ readonly (semantics::data_member&);
+
+ static bool
+ readonly (semantics::class_& c)
+ {
+ return c.count ("readonly");
+ }
+
+ // Null-able.
+ //
+ bool
+ null (data_member_path const&) const;
+
+ bool
+ null (semantics::data_member&) const;
+
+ bool
+ null (semantics::data_member&, string const& key_prefix) const;
+
+ // Optimistic concurrency.
+ //
+ static semantics::data_member*
+ optimistic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::data_member*> ("optimistic-member", 0);
+ }
+
+ static bool
+ version (semantics::data_member& m)
+ {
+ return m.count ("version");
+ }
+
+ static bool
+ version (const data_member_path& mp)
+ {
+ return mp.size () == 1 && mp.back ()->count ("version");
+ }
+
+ // Polymorphic inheritance. Return root of the hierarchy or NULL if
+ // not polymorphic.
+ //
+ static semantics::class_*
+ polymorphic (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return c.get<semantics::class_*> ("polymorphic-root", 0);
+ }
+
+ static semantics::class_&
+ polymorphic_base (semantics::class_& c)
+ {
+ // Set by the validator.
+ //
+ return *c.get<semantics::class_*> ("polymorphic-base");
+ }
+
+ static size_t
+ polymorphic_depth (semantics::class_&);
+
+ static bool
+ discriminator (semantics::data_member& m)
+ {
+ return m.count ("discriminator");
+ }
+
+ static semantics::data_member*
+ discriminator (semantics::class_& c)
+ {
+ // Set by type processor.
+ //
+ return c.get<semantics::data_member*> ("discriminator", 0);
+ }
+
+ // Model version.
+ //
+ bool
+ versioned () const
+ {
+ return unit.count ("model-version") != 0;
+ }
+
+ model_version const&
+ version () const
+ {
+ return unit.get<model_version> ("model-version");
+ }
+
+ // Versioned object, view, or composite.
+ //
+ static bool
+ versioned (semantics::class_& c)
+ {
+ // Set by processor.
+ //
+ return c.count ("versioned") != 0;
+ }
+
+ // Versioned container.
+ //
+ static bool
+ versioned (semantics::data_member& m)
+ {
+ // Set by processor.
+ //
+ return container (m)->count ("versioned");
+ }
+
+ // Object sections.
+ //
+ static object_section&
+ section (semantics::data_member& m)
+ {
+ object_section* s (m.get<object_section*> ("section", 0));
+ return s == 0 ? main_section : *s;
+ }
+
+ static object_section&
+ section (data_member_path const& mp)
+ {
+ // The direct member of the object specifies the section. If the
+ // path is empty (which can happen, for example, for a container
+ // element), assume it is the main section.
+ //
+ //
+ return mp.empty () ? main_section : section (*mp.front ());
+ }
+
+ // Member belongs to a section that is loaded separately.
+ //
+ static bool
+ separate_load (semantics::data_member& m)
+ {
+ return section (m).separate_load ();
+ }
+
+ static bool
+ separate_load (data_member_path const& mp)
+ {
+ return section (mp).separate_load ();
+ }
+
+ // Member belongs to a section that is updated separately.
+ //
+ static bool
+ separate_update (semantics::data_member& m)
+ {
+ return section (m).separate_update ();
+ }
+
+ static bool
+ separate_update (data_member_path const& mp)
+ {
+ return section (mp).separate_update ();
+ }
+
+ //
+ //
+ typedef ::class_kind class_kind_type;
+
+ static class_kind_type
+ class_kind (semantics::class_&);
+
+ // Return class names. For ordinary classes, this will be the class
+ // name itself. For class template instantiations this will be the
+ // typedef name used in the pragma.
+ //
+ static string
+ class_name (semantics::class_&);
+
+ static string
+ class_fq_name (semantics::class_&);
+
+ // Return class scope. For ordinary classes, this will be the scope
+ // where the class is defined. For class template instantiations this
+ // will be the scope of the typedef name used in the pragma.
+ //
+ static semantics::scope&
+ class_scope (semantics::class_&);
+
+ // Return the class file. For ordinary classes, this will be the file
+ // where the class is defined. For class template instantiations this
+ // will be the file containing the pragma.
+ //
+ static semantics::path
+ class_file (semantics::class_&);
+
+ // Return the location (as location_t; useful for comparison) of
+ // an "ODB class" (i.e., an object, view, or composite value),
+ // taking into account things like definition point overrides,
+ // etc.
+ //
+ location_t
+ class_location (semantics::class_&);
+
+ // Same as above, but returns "real" location, that is, ignoring
+ // the definition point overrides.
+ //
+ location_t
+ class_real_location (semantics::class_&);
+
+ // Database names and types.
+ //
+public:
+ // Schema name for a namespace.
+ //
+ qname
+ schema (semantics::scope&) const;
+
+ // Table name prefix for a namespace.
+ //
+ string
+ table_name_prefix (semantics::scope&) const;
+
+ //
+ //
+ struct table_prefix
+ {
+ table_prefix (): level (0), derived (false) {}
+ table_prefix (semantics::class_&);
+
+ void
+ append (semantics::data_member&);
+
+ qname ns_schema; // Object's namespace schema.
+ string ns_prefix; // Object's namespace table prefix.
+ qname prefix;
+ size_t level;
+ bool derived; // One of the components in the prefix was derived.
+ };
+
+ qname
+ table_name (semantics::class_&, bool* derived = 0) const;
+
+ qname
+ table_name (semantics::class_&, data_member_path const&) const;
+
+ // Table name for the container member. The table prefix passed as the
+ // second argument must include the table prefix specified with the
+ // --table-prefix option.
+ //
+ qname
+ table_name (semantics::data_member&, table_prefix const&) const;
+
+ string
+ table_options (semantics::class_&);
+
+ // Table options for the container member.
+ //
+ string
+ table_options (semantics::data_member&, semantics::type& ct);
+
+ //
+ //
+ struct column_prefix
+ {
+ column_prefix (): derived (false), underscore (false) {}
+
+ column_prefix (semantics::data_member& m,
+ string const& key_prefix = string (),
+ string const& default_name = string ())
+ : derived (false), underscore (false)
+ {
+ append (m, key_prefix, default_name);
+ }
+
+ // If the last argument is true, the prefix will include the last member
+ // in the path.
+ //
+ column_prefix (data_member_path const&, bool last = false);
+
+ bool
+ empty () const {return prefix.empty ();}
+
+ void
+ append (semantics::data_member&,
+ string const& key_prefix = string (),
+ string const& default_name = string ());
+
+ string prefix;
+ bool derived; // One of the components in the prefix was derived.
+ bool underscore; // Trailing underscore was automatically added.
+ };
+
+ string
+ column_name (semantics::data_member&, bool& derived) const;
+
+ string
+ column_name (semantics::data_member&, column_prefix const&) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ bool& derived) const;
+
+ string
+ column_name (semantics::data_member&,
+ string const& key_prefix,
+ string const& default_name,
+ column_prefix const&) const;
+
+ string
+ column_name (data_member_path const&) const;
+
+ //
+ //
+ string
+ column_type (const data_member_path&,
+ string const& key_prefix = string (),
+ bool id = false); // Pass true if this type is object id other
+ // than because of the members in the path.
+ string
+ column_type (semantics::data_member&, string const& key_prefix = string ());
+
+ string
+ column_options (semantics::data_member&);
+
+ string
+ column_options (semantics::data_member&, string const& key_prefix);
+
+ // Cleaned-up member name that can be used for database names.
+ //
+ string
+ public_name_db (semantics::data_member&) const;
+
+ // Compose the name by inserting/removing an underscore, as necessary.
+ //
+ static string
+ compose_name (string const& prefix, string const& name);
+
+ // SQL name transformations.
+ //
+ enum sql_name_type
+ {
+ sql_name_all,
+ sql_name_table,
+ sql_name_column,
+ sql_name_index,
+ sql_name_fkey,
+ sql_name_sequence,
+ sql_name_statement,
+ sql_name_count
+ };
+
+ string
+ transform_name (string const& name, sql_name_type) const;
+
+ // C++ names.
+ //
+public:
+ // Cleaned-up and potentially escaped member name that can be used
+ // in public C++ interfaces.
+ //
+ string
+ public_name (semantics::data_member&, bool escape = true) const;
+
+ // "Flatten" fully-qualified C++ name by replacing '::' with '_'
+ // and removing leading '::', if any.
+ //
+ static string
+ flat_name (string const& fqname);
+
+ // Escape C++ keywords, reserved names, and illegal characters.
+ //
+ string
+ escape (string const&) const;
+
+ // Make C++ include guard name by split words, e.g., "FooBar" to
+ // "Foo_Bar" and converting everything to upper case.
+ //
+ string
+ make_guard (string const&) const;
+
+ // Return a string literal that can be used in C++ source code. It
+ // includes "".
+ //
+ static string
+ strlit (string const&);
+
+public:
+ // Generate explicit instantiation headers with all the necessary
+ // extern and export symbols.
+ //
+ void
+ inst_header (bool decl, bool omit_exp = false);
+
+ // Counts and other information.
+ //
+public:
+ struct column_count_type
+ {
+ column_count_type ()
+ : total (0),
+ id (0),
+ inverse (0),
+ readonly (0),
+ optimistic_managed (0),
+ discriminator (0),
+ added (0),
+ deleted (0),
+ soft (0),
+ separate_load (0),
+ separate_update (0)
+ {
+ }
+
+ size_t total;
+ size_t id;
+ size_t inverse;
+ size_t readonly;
+ size_t optimistic_managed;
+ size_t discriminator;
+
+ size_t added; // Soft-added.
+ size_t deleted; // Soft-deleted.
+ size_t soft; // Soft-added/deleted (a column can be both).
+
+ size_t separate_load;
+ size_t separate_update; // Only readwrite.
+ };
+
+ static column_count_type
+ column_count (semantics::class_&, object_section* = 0);
+
+ static data_member_path*
+ id_member (semantics::class_& c)
+ {
+ // Set by the processor. May not be there for reuse-abstract
+ // classes or classes without object id.
+ //
+ return c.count ("id-member") ? &c.get<data_member_path> ("id-member") : 0;
+ }
+
+ // Object pointer information.
+ //
+public:
+ typedef ::pointer_kind pointer_kind_type;
+
+ pointer_kind_type
+ pointer_kind (semantics::type& p)
+ {
+ return p.get<pointer_kind_type> ("pointer-kind");
+ }
+
+ bool
+ lazy_pointer (semantics::type& p)
+ {
+ return p.get<bool> ("pointer-lazy");
+ }
+
+ bool
+ weak_pointer (semantics::type& p)
+ {
+ return pointer_kind (p) == pk_weak;
+ }
+
+ static data_member_path*
+ inverse (semantics::data_member& m)
+ {
+ return object_pointer (utype (m)) && m.count ("inverse")
+ ? &m.get<data_member_path> ("inverse")
+ : 0;
+ }
+
+ data_member_path*
+ inverse (semantics::data_member& m, string const& key_prefix)
+ {
+ if (key_prefix.empty ())
+ return inverse (m);
+
+ if (!object_pointer (utype (m, key_prefix)))
+ return 0;
+
+ string k (key_prefix + "-inverse");
+ return m.count (k) ? &m.get<data_member_path> (k) : 0;
+ }
+
+ // Container information.
+ //
+public:
+ typedef ::container_kind container_kind_type;
+
+ static container_kind_type
+ container_kind (semantics::type& c)
+ {
+ return c.get<container_kind_type> ("container-kind");
+ }
+
+ static bool
+ container_smart (semantics::type& c)
+ {
+ return c.get<bool> ("container-smart");
+ }
+
+ static semantics::type&
+ container_idt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "id", trans);
+ }
+
+ static semantics::type&
+ container_vt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "value", trans);
+ }
+
+ static semantics::type&
+ container_it (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "index", trans);
+ }
+
+ static semantics::type&
+ container_kt (semantics::data_member& m, const custom_cxx_type** trans = 0)
+ {
+ return utype (m, "key", trans);
+ }
+
+ static bool
+ unordered (semantics::data_member& m)
+ {
+ if (m.count ("unordered"))
+ return true;
+
+ if (semantics::type* c = container (m))
+ return c->count ("unordered");
+
+ return false;
+ }
+
+ // The 'is a' and 'has a' tests. The has_a() test currently does not
+ // cross the container boundaries.
+ //
+public:
+ static unsigned short const test_pointer = 0x01;
+ static unsigned short const test_eager_pointer = 0x02;
+ static unsigned short const test_lazy_pointer = 0x04;
+ static unsigned short const test_container = 0x08;
+ static unsigned short const test_straight_container = 0x10;
+ static unsigned short const test_inverse_container = 0x20;
+ static unsigned short const test_readonly_container = 0x40;
+ static unsigned short const test_readwrite_container = 0x80;
+ static unsigned short const test_smart_container = 0x100;
+
+ // Exclude versioned containers.
+ //
+ static unsigned short const exclude_versioned = 0x200;
+
+ // Treat eager loaded members as belonging to the main section.
+ // If this flag is specified, then section must be main_section.
+ //
+ static unsigned short const include_eager_load = 0x800;
+
+ // Exclude added/deleted members.
+ //
+ static unsigned short const exclude_added = 0x1000;
+ static unsigned short const exclude_deleted = 0x2000;
+
+ // By default the test goes into bases for non-polymorphic
+ // hierarchies and doesn't go for polymorphic. The following
+ // flags can be used to alter this behavior.
+ //
+ static unsigned short const exclude_base = 0x4000;
+ static unsigned short const include_base = 0x8000;
+
+ bool
+ is_a (data_member_path const& mp,
+ data_member_scope const& ms,
+ unsigned short flags)
+ {
+ return is_a (mp, ms, flags, utype (*mp.back ()), "");
+ }
+
+ bool
+ is_a (data_member_path const&,
+ data_member_scope const&,
+ unsigned short flags,
+ semantics::type&,
+ string const& key_prefix);
+
+ // Return the number of matching entities. Can be used as a just
+ // a bool value (0 means no match).
+ //
+ size_t
+ has_a (semantics::class_&, unsigned short flags, object_section* = 0);
+
+public:
+ // Process include path by adding the prefix, putting it through
+ // the include regex list, and adding opening and closing include
+ // characters ("" or <>) if necessary. The prefix argument indicates
+ // whether the include prefix specified with the --include-prefix
+ // option should be added. The open argument can be used to specify
+ // the opening character. It can have three values: ", <, or \0. In
+ // case of \0, the character is determined based on the value of the
+ // --include-with-bracket option.
+ //
+ string
+ process_include_path (string const&, bool prefix = true, char open = '\0');
+
+ // Diverge output.
+ //
+public:
+ void
+ diverge (std::ostream& os)
+ {
+ diverge (os.rdbuf ());
+ }
+
+ void
+ diverge (std::streambuf* sb);
+
+ void
+ restore ();
+
+ // Implementation details.
+ //
+private:
+ static bool
+ composite_ (semantics::class_&);
+
+ template <typename X>
+ static X
+ indirect_value (semantics::context const& c, string const& key)
+ {
+ typedef X (*func) ();
+ std::type_info const& ti (c.type_info (key));
+
+ if (ti == typeid (func))
+ return c.get<func> (key) ();
+ else
+ return c.get<X> (key);
+ }
+
+ static semantics::type*
+ indirect_type (semantics::context const& c,
+ string const& kp,
+ semantics::names*& hint)
+ {
+ typedef semantics::type* (*func) (semantics::names*&);
+
+ string const tk (kp + "-tree-type");
+ std::type_info const& ti (c.type_info (tk));
+
+ if (ti == typeid (func))
+ return c.get<func> (tk) (hint);
+ else
+ {
+ hint = c.get<semantics::names*> (kp + "-tree-hint");
+ return c.get<semantics::type*> (tk);
+ }
+ }
+
+public:
+ typedef std::set<string> keyword_set_type;
+
+ struct db_type_type
+ {
+ db_type_type () {}
+ db_type_type (string const& t, string const& it, bool n)
+ : type (t), id_type (it), null (n)
+ {
+ }
+
+ string type;
+ string id_type;
+ bool null;
+ };
+
+ struct type_map_type: std::map<string, db_type_type>
+ {
+ typedef std::map<string, db_type_type> base;
+
+ const_iterator
+ find (semantics::type&, semantics::names* hint);
+ };
+
+protected:
+ struct data
+ {
+ virtual
+ ~data () {}
+
+ data (std::ostream& os)
+ : extra_ (0),
+ os_ (os.rdbuf ()),
+ in_comment_ (false),
+ top_object_ (0),
+ cur_object_ (0),
+ sql_name_upper_ ("(.+)", "\\U$1"),
+ sql_name_lower_ ("(.+)", "\\L$1")
+ {
+ }
+
+ public:
+ void* extra_;
+
+ std::ostream os_;
+ std::stack<std::streambuf*> os_stack_;
+
+ bool in_comment_;
+
+ semantics::class_* top_object_;
+ semantics::class_* cur_object_;
+
+ string exp_;
+ string ext_;
+
+ keyword_set_type keyword_set_;
+ type_map_type type_map_;
+
+ regex_mapping sql_name_regex_[sql_name_count];
+ regexsub sql_name_upper_;
+ regexsub sql_name_lower_;
+
+ regex_mapping include_regex_;
+ regex_mapping accessor_regex_;
+ regex_mapping modifier_regex_;
+ };
+
+ typedef cutl::shared_ptr<data> data_ptr;
+ data_ptr data_;
+
+public:
+ typedef ::features features_type;
+
+ void*& extra; // Extra data that may need to be shared by a sub-system.
+
+ std::ostream& os;
+ semantics::unit& unit;
+ options_type const& options;
+ features_type& features;
+ database const db;
+
+ bool& in_comment;
+
+ string& exp; // Export symbol (with trailing space if specified).
+ string& ext; // Extern symbol.
+
+ keyword_set_type const& keyword_set;
+
+ regex_mapping const& include_regex;
+ regex_mapping const& accessor_regex;
+ regex_mapping const& modifier_regex;
+
+ bool embedded_schema;
+ bool separate_schema;
+
+ bool multi_static;
+ bool multi_dynamic;
+
+ bool force_versioned; // Force statement processing for debugging.
+
+ // Outermost object or view currently being traversed.
+ //
+ semantics::class_*& top_object;
+
+ // Object or view currently being traversed. It can be the same as
+ // top_object or it can a base of top_object.
+ //
+ semantics::class_*& cur_object;
+
+ // Per-database customizable functionality.
+ //
+protected:
+ // Return empty string if there is no mapping. The type passed is
+ // already cvr-unqualified. The null out argument indicates whether
+ // the column should allow NULL values by default.
+ //
+ string
+ database_type (semantics::type& t,
+ semantics::names* hint,
+ bool id,
+ bool* null = 0)
+ {
+ return current ().database_type_impl (t, hint, id, null);
+ }
+
+ // The default implementation uses the type map (populated by the database-
+ // specific context implementation) to come up with a mapping.
+ //
+ virtual string
+ database_type_impl (semantics::type&, semantics::names*, bool, bool*);
+
+public:
+ typedef context root_context;
+
+ virtual
+ ~context ();
+ context ();
+ context (std::ostream&,
+ semantics::unit&,
+ options_type const&,
+ features_type&,
+ data_ptr = data_ptr ());
+
+ static context&
+ current ()
+ {
+ return *current_;
+ }
+
+private:
+ static context* current_;
+
+#if __cplusplus >= 201103L || defined(__GXX_EXPERIMENTAL_CXX0X__)
+ context&
+ operator= (context const&) = delete;
+#else
+private:
+ context&
+ operator= (context const&);
+#endif
+};
+
+// Create concrete database context.
+//
+std::unique_ptr<context>
+create_context (std::ostream&,
+ semantics::unit&,
+ options const&,
+ features&,
+ semantics::relational::model*);
+
+// Checks if scope Y names any of X.
+//
+template <typename X, typename Y>
+bool
+has (Y& y)
+{
+ for (semantics::scope::names_iterator i (y.names_begin ()),
+ e (y.names_end ()); i != e; ++i)
+ if (i->named (). template is_a<X> ())
+ return true;
+
+ return false;
+}
+
+#include <odb/context.ixx>
+
+#endif // ODB_CONTEXT_HXX