From b79567fbc72df23f870049652d5f254aba948bea Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 16 Sep 2011 16:03:25 +0200 Subject: Support for views; integrated part --- odb/common.cxx | 159 ++++---- odb/common.hxx | 48 ++- odb/context.cxx | 63 +++- odb/context.hxx | 117 +++++- odb/cxx-lexer.cxx | 110 +++++- odb/cxx-lexer.hxx | 79 +++- odb/cxx-token.hxx | 20 + odb/error.cxx | 78 ++++ odb/error.hxx | 41 +- odb/gcc-fwd.hxx | 3 + odb/lookup.cxx | 129 +++++++ odb/lookup.hxx | 54 +++ odb/makefile | 1 + odb/pragma.cxx | 528 ++++++++++++++++++++++---- odb/relational/context.hxx | 12 + odb/relational/header.hxx | 121 ++++-- odb/relational/inline.hxx | 53 +++ odb/relational/mysql/source.cxx | 26 +- odb/relational/pgsql/schema.cxx | 2 +- odb/relational/pgsql/source.cxx | 11 +- odb/relational/schema.hxx | 8 +- odb/relational/source.cxx | 544 ++++++++++++++++++++++++++ odb/relational/source.hxx | 777 ++++++++++++++++++++++++++++++++++++-- odb/relational/type-processor.cxx | 772 ++++++++++++++++++++++++++++++++++++- odb/semantics/elements.cxx | 2 +- odb/validator.cxx | 32 +- 26 files changed, 3515 insertions(+), 275 deletions(-) create mode 100644 odb/cxx-token.hxx create mode 100644 odb/lookup.cxx create mode 100644 odb/lookup.hxx diff --git a/odb/common.cxx b/odb/common.cxx index 036b491..98f92c7 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -130,47 +130,26 @@ traverse (semantics::data_member& m) if (semantics::class_* comp = context::composite_wrapper (t)) { - string old_prefix, old_table_prefix; + string old_flat_prefix, old_table_prefix, old_member_prefix; - if (om_.build_prefix_) + if (om_.build_flat_prefix_) { - old_prefix = om_.prefix_; - om_.prefix_ += om_.public_name (m); - om_.prefix_ += '_'; + old_flat_prefix = om_.flat_prefix_; + om_.flat_prefix_ += om_.public_name (m); + om_.flat_prefix_ += '_'; + } + + if (om_.build_member_prefix_) + { + old_member_prefix = om_.member_prefix_; + om_.member_prefix_ += m.name (); + om_.member_prefix_ += '.'; } if (om_.build_table_prefix_) { old_table_prefix = om_.table_prefix_.prefix; - - // If the user provided a table prefix, then use it verbatim. Also - // drop the top-level table prefix in this case. - // - if (m.count ("table")) - { - if (om_.table_prefix_.level <= 1) - om_.table_prefix_.prefix = om_.options.table_prefix (); - - om_.table_prefix_.prefix += m.get ("table"); - } - // Otherwise use the member name and add an underscore unless it is - // already there. - // - else - { - string name (om_.public_name_db (m)); - size_t n (name.size ()); - - if (om_.table_prefix_.prefix.empty ()) - om_.table_prefix_.prefix = om_.options.table_prefix (); - - om_.table_prefix_.prefix += name; - - if (n != 0 && name[n - 1] != '_') - om_.table_prefix_.prefix += '_'; - } - - om_.table_prefix_.level++; + append (m, om_.table_prefix_); } om_.traverse_composite_wrapper (&m, *comp, (wrapper (t) ? &t : 0)); @@ -181,8 +160,11 @@ traverse (semantics::data_member& m) om_.table_prefix_.prefix = old_table_prefix; } - if (om_.build_prefix_) - om_.prefix_ = old_prefix; + if (om_.build_flat_prefix_) + om_.flat_prefix_ = old_flat_prefix; + + if (om_.build_member_prefix_) + om_.member_prefix_ = old_member_prefix; } else if (semantics::type* c = context::container_wrapper (t)) { @@ -194,6 +176,41 @@ traverse (semantics::data_member& m) } } +void object_members_base:: +append (semantics::data_member& m, table_prefix& tp) +{ + context& ctx (context::current ()); + + // If the user provided a table prefix, then use it verbatim. Also + // drop the top-level table prefix in this case. + // + if (m.count ("table")) + { + if (tp.level <= 1) + tp.prefix = ctx.options.table_prefix (); + + tp.prefix += m.get ("table"); + } + // Otherwise use the member name and add an underscore unless it is + // already there. + // + else + { + string name (ctx.public_name_db (m)); + size_t n (name.size ()); + + if (tp.prefix.empty ()) + tp.prefix = ctx.options.table_prefix (); + + tp.prefix += name; + + if (n != 0 && name[n - 1] != '_') + tp.prefix += '_'; + } + + tp.level++; +} + // // object_columns_base // @@ -231,21 +248,7 @@ traverse (semantics::data_member& m, string const& key_prefix, string const& default_name) { - bool custom (m.count (key_prefix + "-column")); - string name (member_.column_name (m, key_prefix, default_name)); - - // If the user provided the column prefix, then use it verbatime. - // Otherwise, append the underscore, unless it is already there. - // - member_.prefix_ = name; - - if (!custom) - { - size_t n (name.size ()); - - if (n != 0 && name[n - 1] != '_') - member_.prefix_ += '_'; - } + column_prefix_ = column_prefix (m, key_prefix, default_name); traverse_composite (&m, c); @@ -300,6 +303,37 @@ traverse (semantics::class_& c) flush (); } +string object_columns_base:: +column_prefix (semantics::data_member& m, string const& kp, string const& dn) +{ + bool custom; + string r; + + if (kp.empty ()) + { + custom = m.count ("column"); + r = context::current ().column_name (m); + } + else + { + custom = m.count (kp + "-column"); + r = context::current ().column_name (m, kp, dn); + } + + // If the user provided the column prefix, then use it verbatime. + // Otherwise, append the underscore, unless it is already there. + // + if (!custom) + { + size_t n (r.size ()); + + if (n != 0 && r[n - 1] != '_') + r += '_'; + } + + return r; +} + void object_columns_base::member:: traverse (semantics::data_member& m) { @@ -310,27 +344,12 @@ traverse (semantics::data_member& m) if (semantics::class_* comp = composite_wrapper (t)) { - string old_prefix (prefix_); - - bool custom (m.count ("column")); - string name (column_name (m)); - - // If the user provided the column prefix, then use it verbatime. - // Otherwise, append the underscore, unless it is already there. - // - prefix_ += name; - - if (!custom) - { - size_t n (name.size ()); - - if (n != 0 && name[n - 1] != '_') - prefix_ += '_'; - } + string old_prefix (oc_.column_prefix_); + oc_.column_prefix_ += column_prefix (m); oc_.traverse_composite (&m, *comp); - prefix_ = old_prefix; + oc_.column_prefix_ = old_prefix; } else if (container_wrapper (t)) { @@ -339,7 +358,7 @@ traverse (semantics::data_member& m) } else { - if (oc_.traverse_column (m, prefix_ + column_name (m), first_)) + if (oc_.traverse_column (m, oc_.column_prefix_ + column_name (m), first_)) { if (first_) first_ = false; diff --git a/odb/common.hxx b/odb/common.hxx index 60f5e3b..96b0935 100644 --- a/odb/common.hxx +++ b/odb/common.hxx @@ -11,7 +11,8 @@ #include -// Traverse object members recursively by going into composite members. +// Traverse object members recursively by going into bases and +// composite members. // struct object_members_base: traversal::class_, virtual context { @@ -60,20 +61,22 @@ public: object_members_base () : member_ (*this) { - init (false, false); + init (false, false, false); } - object_members_base (bool build_prefix, bool build_table_prefix) + object_members_base (bool build_flat_prefix, + bool build_table_prefix, + bool build_member_prefix) : member_ (*this) { - init (build_prefix, build_table_prefix); + init (build_flat_prefix, build_table_prefix, build_member_prefix); } object_members_base (object_members_base const& x) : context (), //@@ -Wextra member_ (*this) { - init (x.build_prefix_, x.build_table_prefix_); + init (x.build_flat_prefix_, x.build_table_prefix_, x.build_member_prefix_); } virtual void @@ -84,16 +87,26 @@ public: virtual void traverse (semantics::data_member&, semantics::class_& comp); +public: + // Append composite member prefix. + // + static void + append (semantics::data_member&, table_prefix&); + protected: - std::string prefix_; - context::table_prefix table_prefix_; + std::string flat_prefix_; + table_prefix table_prefix_; + std::string member_prefix_; private: void - init (bool build_prefix, bool build_table_prefix) + init (bool build_flat_prefix, + bool build_table_prefix, + bool build_member_prefix) { - build_prefix_ = build_prefix; + build_flat_prefix_ = build_flat_prefix; build_table_prefix_ = build_table_prefix; + build_member_prefix_ = build_member_prefix; *this >> names_ >> member_; *this >> inherits_ >> *this; @@ -114,8 +127,9 @@ private: object_members_base& om_; }; - bool build_prefix_; + bool build_flat_prefix_; bool build_table_prefix_; + bool build_member_prefix_; member member_; traversal::names names_; @@ -187,6 +201,18 @@ public: semantics::class_& comp, std::string const& key_prefix, std::string const& default_name); + +public: + // Return column prefix for composite data member. + // + static string + column_prefix (semantics::data_member&, + std::string const& key_prefix = std::string (), + std::string const& default_name = std::string ()); + +protected: + string column_prefix_; + private: void init () @@ -208,8 +234,6 @@ private: public: object_columns_base& oc_; - - string prefix_; bool first_; }; diff --git a/odb/context.cxx b/odb/context.cxx index 03f0308..e679a55 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -3,7 +3,7 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file -#include // std::toupper, std::is{alpha,upper,lower} +#include // std::toupper #include #include @@ -377,19 +377,43 @@ composite_ (semantics::class_& c) } string context:: -table_name (semantics::class_& t) const +table_name (semantics::class_& c) const { string name (options.table_prefix ()); - if (t.count ("table")) - name += t.get ("table"); + if (c.count ("table")) + name += c.get ("table"); else - name += t.name (); + name += c.name (); return name; } string context:: +table_name (semantics::class_& obj, data_member_path const& mp) const +{ + table_prefix tp (table_name (obj) + "_", 1); + + if (mp.size () == 1) + { + // Container directly in the object. + // + return table_name (*mp.back (), tp); + } + else + { + data_member_path::const_iterator i (mp.begin ()); + + // The last member is the container. + // + for (data_member_path::const_iterator e (mp.end () - 1); i != e; ++i) + object_members_base::append (**i, tp); + + return table_name (**i, tp); + } +} + +string context:: table_name (semantics::data_member& m, table_prefix const& p) const { // The table prefix passed as the second argument must include @@ -425,16 +449,39 @@ string context:: column_name (semantics::data_member& m) const { if (m.count ("column")) - return m.get ("column"); - else if (m.type ().count ("column")) - return m.type ().get ("column"); + return m.get ("column").column; else return public_name_db (m); } string context:: +column_name (data_member_path const& mp) const +{ + // The path can lead to a composite value member and column names for + // such members are derived dynamically using the same derivation + // process as when generating object columns (see object_columns_base). + // + string r; + + for (data_member_path::const_iterator i (mp.begin ()); i != mp.end (); ++i) + { + semantics::data_member& m (**i); + + if (composite_wrapper (m.type ())) + r += object_columns_base::column_prefix (m); + else + r += column_name (m); + } + + return r; +} + +string context:: column_name (semantics::data_member& m, string const& p, string const& d) const { + // A container column name can be specified for the member of for the + // container type. + // string key (p + "-column"); if (m.count (key)) return m.get (key); diff --git a/odb/context.hxx b/odb/context.hxx index ea351de..62054a5 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -69,15 +70,22 @@ enum class_kind 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. +// +typedef std::vector data_member_path; + +// // Semantic graph context types. // -struct view_object -{ - tree node; - std::string name; - semantics::class_* object; -}; +// +// struct default_value { enum kind_type @@ -95,6 +103,87 @@ struct default_value tree node; }; +// +// +struct view_object +{ + // Return an alias or unqualified object name. + // + std::string + name () const + { + return alias.empty () ? object->name () : alias; + } + + tree node; + std::string orig_name; // Original name as specified in the pragma. + std::string alias; + tree scope; + location_t loc; + semantics::class_* object; + + cxx_tokens cond; // Join condition tokens. +}; + +typedef std::vector view_objects; + +typedef std::map view_alias_map; +typedef std::map view_object_map; + +// +// +struct view_query +{ + enum kind_type + { + runtime, + complete, + condition + }; + + kind_type kind; + std::string literal; + cxx_tokens expr; + tree scope; + location_t loc; +}; + +// +// +struct table_column +{ + std::string table; + std::string column; + bool expr; // True if column is an expression, and therefore should not + // be quoted. +}; + +// +// +struct column_expr_part +{ + enum kind_type + { + literal, + reference + }; + + kind_type kind; + std::string value; + std::string 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 +{ + location_t loc; +}; + class context { public: @@ -232,10 +321,9 @@ public: string table_name (semantics::class_&) 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. - // + string + table_name (semantics::class_&, data_member_path const&) const; + struct table_prefix { table_prefix (): level (0) {} @@ -245,6 +333,10 @@ public: size_t level; }; + // 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. + // string table_name (semantics::data_member&, table_prefix const&) const; @@ -252,6 +344,9 @@ public: column_name (semantics::data_member&) const; string + column_name (data_member_path const&) const; + + string column_name (semantics::data_member&, string const& key_prefix, string const& default_name) const; @@ -336,7 +431,7 @@ public: return pointer_kind (p) == pk_weak; } - semantics::data_member* + static semantics::data_member* inverse (semantics::data_member& m) { return object_pointer (m.type ()) diff --git a/odb/cxx-lexer.cxx b/odb/cxx-lexer.cxx index 86086bd..7076e8b 100644 --- a/odb/cxx-lexer.cxx +++ b/odb/cxx-lexer.cxx @@ -16,6 +16,10 @@ using namespace std; +// +// cxx_lexer +// + // Token spelling. See cpplib.h for details. // #define OP(e, s) s , @@ -24,6 +28,98 @@ char const* cxx_lexer::token_spelling[N_TTYPES + 1] = { TTYPE_TABLE "KEYWORD"}; #undef OP #undef TK +cxx_lexer:: +~cxx_lexer () +{ +} + +// +// cxx_tokens_lexer +// + +void cxx_tokens_lexer:: +start (cxx_tokens const& ts) +{ + tokens_ = &ts; + cur_ = ts.begin (); +} + +cpp_ttype cxx_tokens_lexer:: +next (std::string& token) +{ + if (cur_ != tokens_->end ()) + { + token = cur_->literal; + return static_cast (cur_++->type); + } + else + return CPP_EOF; +} + +// +// cxx_pragma_lexer +// + +void cxx_pragma_lexer:: +start () +{ + token_ = &token_data_; + type_ = &type_data_; +} + +string cxx_pragma_lexer:: +start (tree& token, cpp_ttype& type) +{ + token_ = &token; + type_ = &type; + + return translate (); +} + +cpp_ttype cxx_pragma_lexer:: +next (string& token) +{ + *type_ = pragma_lex (token_); + token = translate (); + return *type_; +} + +string cxx_pragma_lexer:: +translate () +{ + string r; + + switch (*type_) + { + case CPP_NAME: + { + r = IDENTIFIER_POINTER (*token_); + + // See if this is a keyword using the C++ parser machinery and + // the current C++ dialect. + // + tree id (get_identifier (r.c_str ())); + + if (C_IS_RESERVED_WORD (id)) + *type_ = CPP_KEYWORD; + break; + } + case CPP_STRING: + { + r = TREE_STRING_POINTER (*token_); + break; + } + default: + break; + } + + return r; +} + +// +// cxx_string_lexer +// + // Diagnostics callback. // extern "C" bool @@ -65,7 +161,7 @@ cpp_error_callback ( vfprintf (stderr, msg, *ap); fprintf (stderr, "\n"); - // By resetting the error callback we indicate to cxx_lexer + // By resetting the error callback we indicate to cxx_string_lexer // that there was an error. // cpp_get_callbacks (reader)->error = 0; @@ -75,8 +171,8 @@ cpp_error_callback ( return false; } -cxx_lexer:: -cxx_lexer () +cxx_string_lexer:: +cxx_string_lexer () : reader_ (0) { linemap_init (&line_map_); @@ -91,8 +187,8 @@ cxx_lexer () callbacks_ = cpp_get_callbacks (reader_); } -cxx_lexer:: -~cxx_lexer () +cxx_string_lexer:: +~cxx_string_lexer () { if (reader_ != 0) cpp_destroy (reader_); @@ -100,7 +196,7 @@ cxx_lexer:: linemap_free (&line_map_); } -void cxx_lexer:: +void cxx_string_lexer:: start (string const& data) { // The previous lexing session should have popped the buffer. @@ -119,7 +215,7 @@ start (string const& data) true); } -cpp_ttype cxx_lexer:: +cpp_ttype cxx_string_lexer:: next (string& token) { token.clear (); diff --git a/odb/cxx-lexer.hxx b/odb/cxx-lexer.hxx index 729f196..e5b117e 100644 --- a/odb/cxx-lexer.hxx +++ b/odb/cxx-lexer.hxx @@ -10,29 +10,96 @@ #include +#include + // A C++ keyword. This is an extension to libcpp token types. // #define CPP_KEYWORD ((cpp_ttype) (N_TTYPES + 1)) -// A thin wrapper around cpp_reader for lexing C++ code fragments. -// class cxx_lexer { public: - cxx_lexer (); + virtual ~cxx_lexer (); public: struct invalid_input {}; + virtual cpp_ttype + next (std::string& token) = 0; + +public: + static char const* token_spelling[N_TTYPES + 1]; +}; + + +// Adapter to scan a saved token sequence. +// +class cxx_tokens_lexer: public cxx_lexer +{ +public: void - start (std::string const&); + start (cxx_tokens const&); - cpp_ttype + virtual cpp_ttype next (std::string& token); +private: + cxx_tokens const* tokens_; + cxx_tokens::const_iterator cur_; +}; + + +// A thin wrapper around the pragma_lex() function that recognizes +// CPP_KEYWORD. +// +class cxx_pragma_lexer: public cxx_lexer +{ public: - static char const* token_spelling[N_TTYPES + 1]; + void + start (); + + // Start with an already extracted (using the pragma_lex() function) + // token. This function translates the CPP_NAME to CPP_KEYWORD if + // necessary and returns the string token. It also uses the passed + // token and type for subsequent calls to next() so after the last + // next() call they will contain the information about the last + // token parsed. + // + std::string + start (tree& token, cpp_ttype& type); + + virtual cpp_ttype + next (std::string& token); + +private: + std::string + translate (); + +private: + tree* token_; + cpp_ttype* type_; + + tree token_data_; + cpp_ttype type_data_; +}; + +// A thin wrapper around cpp_reader for lexing C++ code fragments. +// +class cxx_string_lexer: public cxx_lexer +{ +public: + cxx_string_lexer (); + + virtual + ~cxx_string_lexer (); + +public: + void + start (std::string const&); + + virtual cpp_ttype + next (std::string& token); private: std::string data_; diff --git a/odb/cxx-token.hxx b/odb/cxx-token.hxx new file mode 100644 index 0000000..efd0e85 --- /dev/null +++ b/odb/cxx-token.hxx @@ -0,0 +1,20 @@ +// file : odb/cxx-token.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_CXX_TOKEN_HXX +#define ODB_CXX_TOKEN_HXX + +#include +#include + +struct cxx_token +{ + unsigned int type; // Untyped cpp_ttype. + std::string literal; // Only used for name, string, number, etc. +}; + +typedef std::vector cxx_tokens; + +#endif // ODB_CXX_TOKEN_HXX diff --git a/odb/error.cxx b/odb/error.cxx index 5dab2a9..05a30ca 100644 --- a/odb/error.cxx +++ b/odb/error.cxx @@ -3,6 +3,7 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include #include using namespace std; @@ -10,12 +11,32 @@ using namespace std; std::ostream& error (cutl::fs::path const& p, size_t line, size_t clmn) { + //@@ We only need to do this if we are still parsing (i.e., + // pragma parsing). Is there a way to detect this? + // errorcount++; + cerr << p << ':' << line << ':' << clmn << ": error: "; return cerr; } std::ostream& +warn (cutl::fs::path const& p, size_t line, size_t clmn) +{ + warningcount++; + + cerr << p << ':' << line << ':' << clmn << ": warning: "; + return cerr; +} + +std::ostream& +info (cutl::fs::path const& p, size_t line, size_t clmn) +{ + cerr << p << ':' << line << ':' << clmn << ": info: "; + return cerr; +} + +std::ostream& error (location_t loc) { errorcount++; @@ -25,3 +46,60 @@ error (location_t loc) << " error: "; return cerr; } + +std::ostream& +warn (location_t loc) +{ + warningcount++; + cerr << LOCATION_FILE (loc) << ':' + << LOCATION_LINE (loc) << ':' + << LOCATION_COLUMN (loc) << ':' + << " warning: "; + return cerr; +} + +std::ostream& +info (location_t loc) +{ + cerr << LOCATION_FILE (loc) << ':' + << LOCATION_LINE (loc) << ':' + << LOCATION_COLUMN (loc) << ':' + << " info: "; + return cerr; +} + +std::ostream& +error () +{ + return error (input_location); +} + +std::ostream& +warn () +{ + return warn (input_location); +} + +std::ostream& +info () +{ + return info (input_location); +} + +cutl::fs::path +location_file (location_t loc) +{ + return cutl::fs::path (LOCATION_FILE (loc)); +} + +size_t +location_line (location_t loc) +{ + return LOCATION_LINE (loc); +} + +size_t +location_column (location_t loc) +{ + return LOCATION_COLUMN (loc); +} diff --git a/odb/error.hxx b/odb/error.hxx index 2c3bd68..781cbc8 100644 --- a/odb/error.hxx +++ b/odb/error.hxx @@ -6,8 +6,9 @@ #ifndef ODB_ERROR_HXX #define ODB_ERROR_HXX -#include +#include +#include #include #include @@ -15,15 +16,41 @@ using std::endl; std::ostream& -error (cutl::fs::path const&, size_t line, size_t clmn); +error (cutl::fs::path const&, std::size_t line, std::size_t clmn); + +std::ostream& +warn (cutl::fs::path const&, std::size_t line, std::size_t clmn); + +std::ostream& +info (cutl::fs::path const&, std::size_t line, std::size_t clmn); std::ostream& error (location_t); -inline std::ostream& -error () -{ - return error (input_location); -} +std::ostream& +warn (location_t); + +std::ostream& +info (location_t); + +std::ostream& +error (); + +std::ostream& +warn (); + +std::ostream& +info (); + +// location_t macro wrappers. +// +cutl::fs::path +location_file (location_t); + +std::size_t +location_line (location_t); + +std::size_t +location_column (location_t); #endif // ODB_ERROR_HXX diff --git a/odb/gcc-fwd.hxx b/odb/gcc-fwd.hxx index 170be53..ae476c8 100644 --- a/odb/gcc-fwd.hxx +++ b/odb/gcc-fwd.hxx @@ -9,6 +9,9 @@ extern "C" { #include + +typedef unsigned int source_location; // +typedef source_location location_t; // } #endif // ODB_GCC_FWD_HXX diff --git a/odb/lookup.cxx b/odb/lookup.cxx new file mode 100644 index 0000000..4f87203 --- /dev/null +++ b/odb/lookup.cxx @@ -0,0 +1,129 @@ +// file : odb/lookup.cxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#include + +using namespace std; + +namespace lookup +{ + std::string + parse_scoped_name (std::string& t, cpp_ttype& tt, cxx_lexer& lex) + { + string name; + + if (tt == CPP_SCOPE) + { + name += "::"; + tt = lex.next (t); + } + + while (true) + { + // @@ We still need to handle fundamental types, e.g., unsigned int. + // + if (tt != CPP_NAME && tt != CPP_KEYWORD) + throw invalid_name (); + + name += t; + tt = lex.next (t); + + if (tt != CPP_SCOPE) + break; + + name += "::"; + tt = lex.next (t); + } + + return name; + } + + tree + resolve_scoped_name (string& t, + cpp_ttype& tt, + cpp_ttype& ptt, + cxx_lexer& lex, + tree scope, + string& name, + bool is_type, + tree* end_scope) + { + tree id; + bool first (true); + + if (tt == CPP_SCOPE) + { + name += "::"; + scope = global_namespace; + first = false; + + ptt = tt; + tt = lex.next (t); + } + + while (true) + { + if (end_scope != 0) + *end_scope = scope; + + // @@ We still need to handle fundamental types, e.g., unsigned int. + // + if (tt != CPP_NAME && tt != CPP_KEYWORD) + throw invalid_name (); + + name += t; + id = get_identifier (t.c_str ()); + ptt = tt; + tt = lex.next (t); + + bool last (tt != CPP_SCOPE); + tree decl = lookup_qualified_name (scope, id, last && is_type, false); + + // If this is the first component in the name, then also search the + // outer scopes. + // + if (decl == error_mark_node && first && scope != global_namespace) + { + do + { + scope = TYPE_P (scope) + ? CP_TYPE_CONTEXT (scope) + : CP_DECL_CONTEXT (scope); + decl = lookup_qualified_name (scope, id, last && is_type, false); + } while (decl == error_mark_node && scope != global_namespace); + } + + if (decl == error_mark_node) + throw unable_to_resolve (name, last); + + scope = decl; + + if (last) + break; + + first = false; + + if (TREE_CODE (scope) == TYPE_DECL) + scope = TREE_TYPE (scope); + + name += "::"; + + ptt = tt; + tt = lex.next (t); + } + + // Get the actual type if this is a TYPE_DECL. + // + if (is_type) + { + if (TREE_CODE (scope) == TYPE_DECL) + scope = TREE_TYPE (scope); + + scope = TYPE_MAIN_VARIANT (scope); + } + + return scope; + } +} diff --git a/odb/lookup.hxx b/odb/lookup.hxx new file mode 100644 index 0000000..6c3acff --- /dev/null +++ b/odb/lookup.hxx @@ -0,0 +1,54 @@ +// file : odb/lookup.hxx +// author : Boris Kolpackov +// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC +// license : GNU GPL v3; see accompanying LICENSE file + +#ifndef ODB_LOOKUP_HXX +#define ODB_LOOKUP_HXX + +#include + +#include + +#include + +namespace lookup +{ + struct invalid_name + { + }; + + struct unable_to_resolve + { + unable_to_resolve (std::string const& n, bool l): name_ (n), last_ (l) {} + + std::string const& + name () const {return name_;} + + // Last component in the name. + // + bool + last () const {return last_;} + + private: + std::string name_; + bool last_; + }; + + std::string + parse_scoped_name (std::string& token, + cpp_ttype& type, + cxx_lexer& lexer); + + tree + resolve_scoped_name (std::string& token, + cpp_ttype& type, + cpp_ttype& previous_type, + cxx_lexer& lexer, + tree start_scope, + std::string& name, + bool is_type, + tree* end_scope = 0); +} + +#endif // ODB_LOOKUP_HXX diff --git a/odb/makefile b/odb/makefile index 24c83e2..1665faf 100644 --- a/odb/makefile +++ b/odb/makefile @@ -15,6 +15,7 @@ context.cxx \ common.cxx \ emitter.cxx \ error.cxx \ +lookup.cxx \ include.cxx \ header.cxx \ inline.cxx \ diff --git a/odb/pragma.cxx b/odb/pragma.cxx index ea2a696..17eaa42 100644 --- a/odb/pragma.cxx +++ b/odb/pragma.cxx @@ -5,10 +5,14 @@ #include +#include // std::isalnum #include +#include #include +#include #include +#include #include #include @@ -40,96 +44,185 @@ accumulate (compiler::context& ctx, string const& k, any const& v, location_t) loc_pragmas loc_pragmas_; decl_pragmas decl_pragmas_; -static tree -parse_scoped_name (tree& t, - cpp_ttype& tt, - string& name, - bool is_type, - string const& prag) +static bool +parse_expression (tree& t, + cpp_ttype& tt, + cxx_tokens& ts, + string const& prag) { - tree scope, id; - bool first (true); - - if (tt == CPP_SCOPE) - { - name += "::"; - scope = global_namespace; - first = false; - tt = pragma_lex (&t); - } - else - scope = current_scope (); + // Keep reading tokens until we see a matching ')' while keeping track + // of their balance. + // + size_t balance (0); - while (true) + for (; tt != CPP_EOF; tt = pragma_lex (&t)) { - if (tt != CPP_NAME) - { - error () << "invalid name in db pragma " << prag << endl; - return 0; - } - - id = t; - name += IDENTIFIER_POINTER (t); - tt = pragma_lex (&t); - - bool last (tt != CPP_SCOPE); - tree decl = lookup_qualified_name (scope, id, last && is_type, false); + bool done (false); + cxx_token ct; - // If this is the first component in the name, then also search the - // outer scopes. - // - if (decl == error_mark_node && first && scope != global_namespace) + switch (tt) { - do + case CPP_OPEN_PAREN: { - scope = TYPE_P (scope) - ? CP_TYPE_CONTEXT (scope) - : CP_DECL_CONTEXT (scope); - decl = lookup_qualified_name (scope, id, last && is_type, false); - } while (decl == error_mark_node && scope != global_namespace); - } - - if (decl == error_mark_node) - { - if (last) + balance++; + break; + } + case CPP_CLOSE_PAREN: { - error () << "unable to resolve " << (is_type ? "type " : "") << "name " - << "'" << name << "' in db pragma " << prag << endl; + if (balance == 0) + done = true; + else + balance--; + break; } - else + case CPP_STRING: { - error () << "unable to resolve name '" << name << "' in db pragma " - << prag << endl; + ct.literal = TREE_STRING_POINTER (t); + break; } + case CPP_NAME: + { + ct.literal = IDENTIFIER_POINTER (t); + break; + } + case CPP_NUMBER: + { + switch (TREE_CODE (t)) + { + case INTEGER_CST: + { + tree type (TREE_TYPE (t)); + + HOST_WIDE_INT hwl (TREE_INT_CST_LOW (t)); + HOST_WIDE_INT hwh (TREE_INT_CST_HIGH (t)); + + unsigned long long l (hwl); + unsigned long long h (hwh); + unsigned short width (HOST_BITS_PER_WIDE_INT); + + unsigned long long v ((h << width) + l); + + ostringstream os; + os << v; + + if (type == long_long_integer_type_node) + os << "LL"; + else if (type == long_long_unsigned_type_node) + os << "ULL"; + else if (type == long_integer_type_node) + os << "L"; + else if (type == long_unsigned_type_node) + os << "UL"; + else if ( + TYPE_UNSIGNED (type) && + TYPE_PRECISION (type) >= TYPE_PRECISION (integer_type_node)) + os << "U"; + + ct.literal = os.str (); + break; + } + case REAL_CST: + { + tree type (TREE_TYPE (t)); + REAL_VALUE_TYPE val (TREE_REAL_CST (t)); + + // This is the best we can do. val cannot be INF or NaN. + // + char tmp[256]; + real_to_decimal (tmp, &val, sizeof (tmp), 0, true); + istringstream is (tmp); + ostringstream os; + + if (type == float_type_node) + { + float f; + is >> f; + os << f << 'F'; + } + else + { + double d; + is >> d; + os << d; + } + + ct.literal = os.str (); + break; + } + default: + { + error () + << "unexpected numeric constant in db pragma " << prag << endl; + return false; + } + } - return 0; + break; + } + default: + { + break; + } } - scope = decl; - - if (last) + if (done) break; - first = false; + ct.type = tt; + ts.push_back (ct); + } - if (TREE_CODE (scope) == TYPE_DECL) - scope = TREE_TYPE (scope); + return true; +} - name += "::"; - tt = pragma_lex (&t); - } - // Get the actual type if this is a TYPE_DECL. - // - if (is_type) +static string +parse_scoped_name (tree& token, cpp_ttype& type, string const& prag) +{ + try + { + cxx_pragma_lexer lex; + string st (lex.start (token, type)); + return lookup::parse_scoped_name (st, type, lex); + } + catch (lookup::invalid_name const&) { - if (TREE_CODE (scope) == TYPE_DECL) - scope = TREE_TYPE (scope); + error () << "invalid name in db pragma " << prag << endl; + return ""; + } +} - scope = TYPE_MAIN_VARIANT (scope); +static tree +resolve_scoped_name (tree& token, + cpp_ttype& type, + string& name, + bool is_type, + string const& prag) +{ + try + { + cxx_pragma_lexer lex; + cpp_ttype ptt; // Not used. + string st (lex.start (token, type)); + return lookup::resolve_scoped_name ( + st, type, ptt, lex, current_scope (), name, is_type); + } + catch (lookup::invalid_name const&) + { + error () << "invalid name in db pragma " << prag << endl; + return 0; } + catch (lookup::unable_to_resolve const& e) + { + if (e.last ()) + error () << "unable to resolve " << (is_type ? "type " : "") << "name " + << "'" << e.name () << "' in db pragma " << prag << endl; + else + error () << "unable to resolve name '" << e.name () << "' in db pragma " + << prag << endl; - return scope; + return 0; + } } static bool @@ -511,7 +604,9 @@ handle_pragma (cpp_reader* reader, } else if (p == "query") { + // query () // query ("statement") + // query (expr) // // Make sure we've got the correct declaration type. @@ -527,25 +622,59 @@ handle_pragma (cpp_reader* reader, tt = pragma_lex (&t); - if (tt != CPP_STRING) + view_query vq; + + bool s (false); + string str; + + if (tt == CPP_STRING) { - error () << "query statement expected in db pragma " << p << endl; - return; + s = true; + str = TREE_STRING_POINTER (t); + tt = pragma_lex (&t); } - val = string (TREE_STRING_POINTER (t)); + if (tt == CPP_CLOSE_PAREN) + { + if (s) + vq.literal = str; + else + { + // Empty query() pragma indicates that the statement will be + // provided at runtime. Encode this case as empty literal + // and expression. + // + } + } + else + { + // Expression. + // + if (s) + { + vq.expr.push_back (cxx_token ()); + vq.expr.back ().type = CPP_STRING; + vq.expr.back ().literal = str; + } - if (pragma_lex (&t) != CPP_CLOSE_PAREN) + if (!parse_expression (t, tt, vq.expr, p)) + return; // Diagnostics has already been issued. + } + + if (tt != CPP_CLOSE_PAREN) { error () << "')' expected at the end of db pragma " << p << endl; return; } + vq.scope = current_scope (); + vq.loc = loc; + val = vq; tt = pragma_lex (&t); } else if (p == "object") { - // object (name) + // object (fq-name [ = name] [: expr]) // // Make sure we've got the correct declaration type. @@ -568,17 +697,50 @@ handle_pragma (cpp_reader* reader, } view_object vo; - vo.node = parse_scoped_name (t, tt, vo.name, true, p); + vo.node = resolve_scoped_name (t, tt, vo.orig_name, true, p); if (vo.node == 0) return; // Diagnostics has already been issued. + if (tt == CPP_EQ) + { + // We have an alias. + // + if (pragma_lex (&t) != CPP_NAME) + { + error () << "alias name expected after '=' in db pragma " << p << endl; + return; + } + + vo.alias = IDENTIFIER_POINTER (t); + tt = pragma_lex (&t); + } + + if (tt == CPP_COLON) + { + // We have a condition. + + tt = pragma_lex (&t); + + if (!parse_expression (t, tt, vo.cond, p)) + return; // Diagnostics has already been issued. + + if (vo.cond.empty ()) + { + error () + << "join condition expected after ':' in db pragma " << p << endl; + return; + } + } + if (tt != CPP_CLOSE_PAREN) { error () << "')' expected at the end of db pragma " << p << endl; return; } + vo.scope = current_scope (); + vo.loc = loc; val = vo; name = "objects"; // Change the context entry name. adder = &accumulate; @@ -609,13 +771,217 @@ handle_pragma (cpp_reader* reader, tt = pragma_lex (&t); } - else if (p == "column" || - p == "value_column" || + else if (p == "column") + { + // column ("") + // column (".") + // column (""."") + // column (fq-name) (view only) + // column (expr) (view only) + // + + // Make sure we've got the correct declaration type. + // + if (decl != 0 && !check_spec_decl_type (decl, decl_name, p, loc)) + return; + + if (pragma_lex (&t) != CPP_OPEN_PAREN) + { + error () << "'(' expected after db pragma " << p << endl; + return; + } + + tt = pragma_lex (&t); + + bool s (false); + string str; + + // String can be just the column name, a table name followed by the + // column name, or part of an expression, depending on what comes + // after the string. + // + if (tt == CPP_STRING) + { + s = true; + str = TREE_STRING_POINTER (t); + tt = pragma_lex (&t); + } + + if (tt == CPP_CLOSE_PAREN) + { + if (s) + { + // "" or "." + // + table_column tc; + tc.expr = false; + + // Scan the string and see if we have any non-identifier + // characters. If so, assume it is an expression. While + // at it also see if there is '.'. + // + string::size_type p (string::npos); + + for (size_t i (0); i < str.size (); ++i) + { + char c (str[i]); + + if (!(isalnum (c) || c == '_')) + { + tc.expr = true; + break; + } + + if (c == '.') + { + if (p != string::npos) + { + // Second '.' -- something fishy is going on. + tc.expr = true; + break; + } + + p = i; + } + } + + if (!tc.expr && p != string::npos) + { + tc.table.assign (str, 0, p); + tc.column.assign (str, p + 1, string::npos); + } + else + tc.column = str; + + val = tc; + } + else + { + error () << "column name expected in db pragma " << p << endl; + return; + } + } + else if (tt == CPP_DOT) + { + if (s) + { + // ""."" + // + table_column tc; + tc.expr = false; + + if (pragma_lex (&t) != CPP_STRING) + { + error () << "column name expected after '.' in db pragma " << p + << endl; + return; + } + + tc.table = str; + tc.column = TREE_STRING_POINTER (t); + val = tc; + tt = pragma_lex (&t); + } + else + { + error () << "column name expected in db pragma " << p << endl; + return; + } + } + else + { + // We have an expression. + // + column_expr e; + + if (s) + { + e.push_back (column_expr_part ()); + e.back ().kind = column_expr_part::literal; + e.back ().value = str; + + if (tt != CPP_PLUS) + { + error () << "'+' or ')' expected in db pragma " << p << endl; + return; + } + + tt = pragma_lex (&t); + } + + for (;;) + { + if (tt == CPP_STRING) + { + e.push_back (column_expr_part ()); + e.back ().kind = column_expr_part::literal; + e.back ().value = TREE_STRING_POINTER (t); + + tt = pragma_lex (&t); + } + else if (tt == CPP_NAME || tt == CPP_SCOPE) + { + string name (parse_scoped_name (t, tt, p)); + + if (name.empty ()) + return; // Diagnostics has already been issued. + + // Resolve nested members if any. + // + for (; tt == CPP_DOT; tt = pragma_lex (&t)) + { + if (pragma_lex (&t) != CPP_NAME) + { + error () << "name expected after '.' in db pragma " << p << endl; + return; + } + + name += '.'; + name += IDENTIFIER_POINTER (t); + } + + e.push_back (column_expr_part ()); + e.back ().kind = column_expr_part::reference; + e.back ().value = name; + e.back ().scope = current_scope (); + e.back ().loc = loc; + } + else + { + error () << "string literal or name expected in db pragma " << p + << endl; + return; + } + + if (tt == CPP_PLUS) + tt = pragma_lex (&t); + else if (tt == CPP_CLOSE_PAREN) + break; + else + { + error () << "'+' or ')' expected in db pragma " << p << endl; + return; + } + } + + e.loc = loc; + val = e; + name = "column-expr"; + } + + if (tt != CPP_CLOSE_PAREN) + { + error () << "')' expected at the end of db pragma " << p << endl; + return; + } + + tt = pragma_lex (&t); + } + else if (p == "value_column" || p == "index_column" || p == "key_column" || p == "id_column") { - // column ("") // value_column ("") // index_column ("") // key_column ("") @@ -839,7 +1205,7 @@ handle_pragma (cpp_reader* reader, { // We have a potentially scopped enumerator name. // - dv.node = parse_scoped_name (t, tt, dv.value, false, p); + dv.node = resolve_scoped_name (t, tt, dv.value, false, p); if (dv.node == 0) return; // Diagnostics has already been issued. @@ -1021,7 +1387,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) if (tt == CPP_NAME || tt == CPP_SCOPE) { - decl = parse_scoped_name (t, tt, decl_name, true, p); + decl = resolve_scoped_name (t, tt, decl_name, true, p); if (decl == 0) return; // Diagnostics has already been issued. @@ -1059,7 +1425,7 @@ handle_pragma_qualifier (cpp_reader* reader, string const& p) if (tt == CPP_NAME || tt == CPP_SCOPE) { - decl = parse_scoped_name (t, tt, decl_name, false, p); + decl = resolve_scoped_name (t, tt, decl_name, false, p); if (decl == 0) return; // Diagnostics has already been issued. diff --git a/odb/relational/context.hxx b/odb/relational/context.hxx index e654aee..dfe1f9c 100644 --- a/odb/relational/context.hxx +++ b/odb/relational/context.hxx @@ -47,6 +47,12 @@ namespace relational } string + column_qname (data_member_path const& mp) const + { + return quote_id (column_name (mp)); + } + + string column_qname (semantics::data_member& m, string const& key_prefix, string const& default_name) const @@ -61,6 +67,12 @@ namespace relational } string + table_qname (semantics::class_& obj, data_member_path const& mp) const + { + return quote_id (table_name (obj, mp)); + } + + string table_qname (semantics::data_member& m, table_prefix const& p) const { return quote_id (table_name (m, p)); diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index cc89e9a..4ac919c 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -305,7 +305,7 @@ namespace relational typedef container_traits base; container_traits (semantics::class_& c) - : object_members_base (true, false), c_ (c) + : object_members_base (true, false, false), c_ (c) { } @@ -390,7 +390,7 @@ namespace relational } } - string name (prefix_ + public_name (m) + "_traits"); + string name (flat_prefix_ + public_name (m) + "_traits"); // Figure out column counts. // @@ -1269,20 +1269,82 @@ namespace relational // if (c.count ("objects")) { - /* - typedef std::vector objects; - - objects const& objs (c.get ("objects")); - */ - - /* - os << "struct query_type: query_base_type, query_columns" - << "{" - << "query_type ();" - << "query_type (const std::string&);" - << "query_type (const query_base_type&);" - << "};"; - */ + view_objects& objs (c.get ("objects")); + + if (objs.size () > 1) + { + os << "struct query_columns" + << "{"; + + for (view_objects::const_iterator i (objs.begin ()); + i < objs.end (); + ++i) + { + bool alias (!i->alias.empty ()); + semantics::class_& o (*i->object); + string const& name (alias ? i->alias : o.name ()); + string const& type (o.fq_name ()); + + os << "// " << name << endl + << "//" << endl; + + if (alias && i->alias != table_name (o)) + os << "static const char " << name << "_alias_[];" + << endl + << "typedef" << endl + << "odb::pointer_query_columns< " << type << ", " << + name << "_alias_ >" << endl + << name << ";" + << endl; + else + os << "typedef" << endl + << "odb::pointer_query_columns<" << endl + << " " << type << "," << endl + << " " << "odb::access::object_traits< " << type << + " >::table_name >" << endl + << name << ";" + << endl; + } + + os << "};" + << "struct query_type: query_base_type, query_columns" + << "{"; + } + else + { + // For a single object view we generate a shortcut without + // an intermediate typedef. + // + view_object const& vo (objs[0]); + + bool alias (!vo.alias.empty ()); + semantics::class_& o (*vo.object); + string const& type (o.fq_name ()); + + if (alias && vo.alias != table_name (o)) + os << "static const char query_alias[];" + << endl + << "struct query_type:" << endl + << " query_base_type," << endl + << " odb::pointer_query_columns< " << type << + ", query_alias >" + << "{"; + else + os << "struct query_type:" << endl + << " query_base_type," << endl + << " odb::pointer_query_columns<" << endl + << " " << type << "," << endl + << " odb::access::object_traits< " << type << + " >::table_name >" + << "{"; + } + + os << "query_type ();" + << "query_type (bool);" + << "query_type (const char*);" + << "query_type (const std::string&);" + << "query_type (const query_base_type&);" + << "};"; } else os << "typedef query_base_type query_type;" @@ -1307,7 +1369,7 @@ namespace relational // init (view, image) // os << "static void" << endl - << "init (view_type&, const image_type&);" + << "init (view_type&, const image_type&, database&);" << endl; // column_count @@ -1318,8 +1380,14 @@ namespace relational // Statements. // - os << "static const char query_statement[];" - << endl; + view_query& vq (c.get ("query")); + + if (vq.kind != view_query::runtime) + { + os << "static query_base_type" << endl + << "query_statement (const query_base_type&);" + << endl; + } // // Functions. @@ -1447,17 +1515,20 @@ namespace relational bool abst (abstract (c)); string const& type (c.fq_name ()); - os << "// " << c.name () << endl - << "//" << endl; - if (options.generate_query ()) { + bool has_ptr (has_a (c, test_pointer)); + + if (has_ptr || !abst) + os << "// " << c.name () << endl + << "//" << endl; + // query_columns // // If we don't have any pointers, then query_columns is generated // in pass 1 (see the comment in class1 for details). // - if (has_a (c, test_pointer)) + if (has_ptr) query_columns_type_->traverse (c); // query_type @@ -1469,10 +1540,14 @@ namespace relational << " query_columns< " << type << ", table_name >" << "{" << "query_type ();" + << "query_type (bool);" + << "query_type (const char*);" << "query_type (const std::string&);" << "query_type (const query_base_type&);" << "};"; } + + // Move header comment out of if-block if adding any code here. } virtual void diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index 1134df6..9abc86f 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -215,6 +215,20 @@ namespace relational os << "inline" << endl << traits << "::query_type::" << endl + << "query_type (bool v)" << endl + << " : query_base_type (v)" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type (const char* q)" << endl + << " : query_base_type (q)" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl << "query_type (const std::string& q)" << endl << " : query_base_type (q)" << "{" @@ -258,6 +272,45 @@ namespace relational view_extra (c); + // query_type + // + if (c.count ("objects")) + { + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type ()" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type (bool v)" << endl + << " : query_base_type (v)" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type (const char* q)" << endl + << " : query_base_type (q)" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type (const std::string& q)" << endl + << " : query_base_type (q)" + << "{" + << "}"; + + os << "inline" << endl + << traits << "::query_type::" << endl + << "query_type (const query_base_type& q)" << endl + << " : query_base_type (q)" + << "{" + << "}"; + } + // callback () // os << "inline" << endl diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx index 02bc74b..8f80a5a 100644 --- a/odb/relational/mysql/source.cxx +++ b/odb/relational/mysql/source.cxx @@ -127,7 +127,7 @@ namespace relational } line_ += column; - line_ += "+0, ' ', "; + line_ += "+0,' ',"; if (!table.empty ()) { @@ -142,6 +142,30 @@ namespace relational }; entry object_columns_; + struct view_columns: relational::view_columns, context + { + view_columns (base const& x): base (x) {} + + virtual void + column (semantics::data_member& m, string const& column) + { + // The same idea as in object_columns. + // + if (column_sql_type (m).type != sql_type::ENUM) + { + base::column (m, column); + return; + } + + line_ += "CONCAT("; + line_ += column; + line_ += "+0,' ',"; + line_ += column; + line_ += ")"; + } + }; + entry view_columns_; + // // bind // diff --git a/odb/relational/pgsql/schema.cxx b/odb/relational/pgsql/schema.cxx index a1ef29d..098d6e9 100644 --- a/odb/relational/pgsql/schema.cxx +++ b/odb/relational/pgsql/schema.cxx @@ -178,7 +178,7 @@ namespace relational struct member_create: object_members_base, context { member_create (emitter& e, ostream& os, relational::tables& tables) - : object_members_base (false, true), + : object_members_base (false, true, false), e_ (e), os_ (os), tables_ (tables) diff --git a/odb/relational/pgsql/source.cxx b/odb/relational/pgsql/source.cxx index 28d04e6..dbda7be 100644 --- a/odb/relational/pgsql/source.cxx +++ b/odb/relational/pgsql/source.cxx @@ -1019,10 +1019,10 @@ namespace relational { os << "sts.connection ()," << endl << "query_statement_name," << endl - << "query_statement + q.clause ()," << endl - << "q.parameter_types ()," << endl - << "q.parameter_count ()," << endl - << "q.parameters_binding ()," << endl + << "qs.clause ()," << endl + << "qs.parameter_types ()," << endl + << "qs.parameter_count ()," << endl + << "qs.parameters_binding ()," << endl << "imb"; } @@ -1044,7 +1044,8 @@ namespace relational if (!object (c_) || abstract (c_)) return; - string scope (scope_ + "::" + prefix_ + public_name (m) + "_traits"); + string scope (scope_ + "::" + flat_prefix_ + public_name (m) + + "_traits"); // Statment names. // diff --git a/odb/relational/schema.hxx b/odb/relational/schema.hxx index 3fde82a..8e56ff9 100644 --- a/odb/relational/schema.hxx +++ b/odb/relational/schema.hxx @@ -72,7 +72,9 @@ namespace relational typedef member_drop base; member_drop (emitter& e, ostream& os, std::vector& t) - : object_members_base (false, true), common (e, os), tables_ (t) + : object_members_base (false, true, false), + common (e, os), + tables_ (t) { } @@ -363,7 +365,9 @@ namespace relational typedef member_create base; member_create (emitter& e, ostream& os, std::vector& t) - : object_members_base (false, true), common (e, os), tables_ (t) + : object_members_base (false, true, false), + common (e, os), + tables_ (t) { } diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx index 8384d96..4f8ecfc 100644 --- a/odb/relational/source.cxx +++ b/odb/relational/source.cxx @@ -3,6 +3,11 @@ // copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC // license : GNU GPL v3; see accompanying LICENSE file +#include + +#include +#include + #include #include @@ -12,6 +17,545 @@ namespace relational { namespace source { + static inline void + add_space (string& s) + { + string::size_type n (s.size ()); + if (n != 0 && s[n - 1] != ' ') + s += ' '; + } + + static string + translate_name_trailer (string& t, + cpp_ttype& tt, + cpp_ttype& ptt, + cxx_tokens_lexer& lex) + { + string r; + + for (; tt != CPP_EOF; ptt = tt, tt = lex.next (t)) + { + bool done (false); + + switch (tt) + { + case CPP_SCOPE: + case CPP_DOT: + { + r += cxx_lexer::token_spelling[tt]; + break; + } + default: + { + // Handle CPP_KEYWORD here to avoid a warning (it is not + // part of the cpp_ttype enumeration). + // + if (tt == CPP_NAME || tt == CPP_KEYWORD) + { + // For names like 'foo::template bar'. + // + if (ptt == CPP_NAME || ptt == CPP_KEYWORD) + r += ' '; + + r += t; + } + else + done = true; + + break; + } + } + + if (done) + break; + } + + return r; + } + + static class_::expression + translate_name (string& t, + cpp_ttype& tt, + cpp_ttype& ptt, + cxx_tokens_lexer& lex, + tree scope, + location_t loc, + string const& prag, + bool check_ptr, + view_alias_map const& amap, + view_object_map const& omap) + { + using semantics::data_member; + typedef class_::expression expression; + + bool multi_obj ((amap.size () + omap.size ()) > 1); + + string r ("query_type"); + string name; + + bool fail (false); + context& ctx (context::current ()); + + // This code is quite similar to view_data_members in the type + // processor. + // + try + { + tree decl (0); + view_object* vo (0); + + // Check if this is an alias. + // + if (tt == CPP_NAME) + { + view_alias_map::const_iterator i (amap.find (t)); + + if (i != amap.end ()) + { + if (multi_obj) + { + r += "::"; + r += i->first; + } + + vo = i->second; + fail = true; // This must be a data member. + + // Skip '::'. + // + ptt = tt; + tt = lex.next (t); + + if (tt != CPP_SCOPE) + { + error (loc) + << "member name expected after an alias in db pragma " + << prag << endl; + throw generation_failed (); + } + + ptt = tt; + tt = lex.next (t); + + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex, vo->object->tree_node (), name, false); + } + } + + // 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) object name (the + // member can come from a base class). + // + tree type; + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex, scope, name, false, &type); + + type = TYPE_MAIN_VARIANT (type); + + view_object_map::const_iterator i (omap.find (type)); + + if (i == omap.end ()) + { + // Not an object associated with this view. Assume it + // is some other valid name. + // + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + + vo = i->second; + + if (multi_obj) + { + r += "::"; + r += vo->object->name (); + } + } + + // Check that we have a data member. + // + if (TREE_CODE (decl) != FIELD_DECL) + { + if (fail) + { + error (loc) + << "name '" << name << "' in db pragma " << prag << " " + << "does not refer to a data member" << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + + expression e (vo); + + data_member* m (dynamic_cast (ctx.unit.find (decl))); + + r += "::"; + r += ctx.public_name (*m); + + // Assemble the member path if we may need to return a pointer + // expression. + // + if (check_ptr) + e.member_path.push_back (m); + + fail = true; // Now we definitely fail if anything goes wrong. + + // Finally, resolve nested members if any. + // + for (; tt == CPP_DOT; ptt = tt, tt = lex.next (t)) + { + // Check if this member is actually of a composite value type. + // This is to handle expressions like "object::member.is_null ()" + // correctly. The remaining issue here is that in the future + // is_null()/is_not_null() will be valid for composite values + // as well. + // + if (!context::composite_wrapper (m->type ())) + break; + + ptt = tt; + tt = lex.next (t); + + if (tt != CPP_NAME) + { + error (loc) + << "name expected after '.' in db pragma " << prag << endl; + throw generation_failed (); + } + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + decl = lookup_qualified_name ( + type, get_identifier (t.c_str ()), false, false); + + if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL) + { + error (loc) + << "name '" << t << "' in db pragma " << prag << " does not " + << "refer to a data member" << endl; + throw generation_failed (); + } + + m = dynamic_cast (ctx.unit.find (decl)); + + //@@ Temporarily translate '.' to '::' until the query is changed + // to use '.' for composite member access. + // + r += "::"; + r += ctx.public_name (*m); + + if (check_ptr) + e.member_path.push_back (m); + } + + // If requested, check if this member is a pointer. We only do this + // if there is nothing after this name. + // + if (check_ptr && tt == CPP_EOF) + { + using semantics::type; + + type* t (&m->type ()); + + if (type* c = context::container_wrapper (*t)) + t = &context::container_vt (*c); + + if (context::object_pointer (*t)) + return e; + } + + // Read the remainder of the expression (e.g., '.is_null ()') if + // the member is not composite and we bailed out from the above + // loop. + // + if (tt == CPP_DOT) + r += translate_name_trailer (t, tt, ptt, lex); + + return expression (r); + } + catch (lookup::invalid_name const&) + { + if (fail) + { + error (loc) << "invalid name in db pragma " << prag << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + catch (lookup::unable_to_resolve const& e) + { + if (fail) + { + error (loc) << "unable to resolve name '" << e.name () + << "' in db pragma " << prag << endl; + throw generation_failed (); + } + else + return expression ( + name + translate_name_trailer (t, tt, ptt, lex)); + } + } + + class_::expression class_:: + translate_expression (type& c, + cxx_tokens const& ts, + tree scope, + location_t loc, + string const& prag, + bool* placeholder) + { + // The overall idea is as folows: read in tokens and add them + // to the string. If a token starts a name, try to resolve it + // to an object member (taking into account aliases). If this + // was successful, translate it to the query column reference. + // Otherwise, output it as is. + // + // If the placeholder argument is not NULL, then we need to + // detect the special '(?)' token sequence and replace it + // with the query variable ('q'). + // + expression e (""); + string& r (e.value); + + view_alias_map const& amap (c.get ("alias-map")); + view_object_map const& omap (c.get ("object-map")); + + cxx_tokens_lexer lex; + lex.start (ts); + + string t; + for (cpp_ttype tt (lex.next (t)), ptt (CPP_EOF); tt != CPP_EOF;) + { + // Try to format the expression to resemble the style of the + // generated code. + // + switch (tt) + { + case CPP_NOT: + { + add_space (r); + r += '!'; + break; + } + case CPP_COMMA: + { + r += ", "; + break; + } + case CPP_OPEN_PAREN: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD) + add_space (r); + + r += '('; + break; + } + case CPP_CLOSE_PAREN: + { + r += ')'; + break; + } + case CPP_OPEN_SQUARE: + { + r += '['; + break; + } + case CPP_CLOSE_SQUARE: + { + r += ']'; + break; + } + case CPP_OPEN_BRACE: + { + add_space (r); + r += "{ "; + break; + } + case CPP_CLOSE_BRACE: + { + add_space (r); + r += '}'; + break; + } + case CPP_SEMICOLON: + { + r += ';'; + break; + } + case CPP_ELLIPSIS: + { + add_space (r); + r += "..."; + break; + } + case CPP_PLUS: + case CPP_MINUS: + { + bool unary (ptt != CPP_NAME && + ptt != CPP_SCOPE && + ptt != CPP_NUMBER && + ptt != CPP_STRING && + ptt != CPP_CLOSE_PAREN && + ptt != CPP_PLUS_PLUS && + ptt != CPP_MINUS_MINUS); + + if (!unary) + add_space (r); + + r += cxx_lexer::token_spelling[tt]; + + if (!unary) + r += ' '; + break; + } + case CPP_PLUS_PLUS: + case CPP_MINUS_MINUS: + { + if (ptt != CPP_NAME && + ptt != CPP_CLOSE_PAREN && + ptt != CPP_CLOSE_SQUARE) + add_space (r); + + r += cxx_lexer::token_spelling[tt]; + break; + } + case CPP_DEREF: + case CPP_DEREF_STAR: + case CPP_DOT: + case CPP_DOT_STAR: + { + r += cxx_lexer::token_spelling[tt]; + break; + } + case CPP_STRING: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += strlit (t); + break; + } + case CPP_NUMBER: + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += t; + break; + } + case CPP_SCOPE: + case CPP_NAME: + { + // Start of a name. + // + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + // Check if this is a pointer expression. + // + // If r is not empty, then it means this is not just the + // name. If placeholder is not 0, then we are translating + // a query expression, not a join condition. + // + expression e ( + translate_name ( + t, tt, ptt, lex, + scope, loc, prag, + r.empty () && placeholder == 0, amap, omap)); + + if (e.kind == expression::literal) + r += e.value; + else + return e; + + continue; // We have already extracted the next token. + } + case CPP_QUERY: + { + if (placeholder != 0 && !*placeholder) + { + if (ptt == CPP_OPEN_PAREN) + { + // Get the next token and see if it is ')'. + // + ptt = tt; + tt = lex.next (t); + + if (tt == CPP_CLOSE_PAREN) + { + r += 'q'; + *placeholder = true; + } + else + { + // The same as in the default case. + // + add_space (r); + r += "? "; + } + continue; // We have already gotten the next token. + } + } + // Fall through. + } + default: + { + // Handle CPP_KEYWORD here to avoid a warning (it is not + // part of the cpp_ttype enumeration). + // + if (tt == CPP_KEYWORD) + { + if (ptt == CPP_NAME || + ptt == CPP_KEYWORD || + ptt == CPP_STRING || + ptt == CPP_NUMBER) + add_space (r); + + r += t; + } + else + { + // All the other operators. + // + add_space (r); + r += cxx_lexer::token_spelling[tt]; + r += ' '; + } + break; + } + } + + // + // Watch out for the continue statements above if you add any + // logic here. + // + + ptt = tt; + tt = lex.next (t); + } + + return e; + } + void generate () { diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index f83ad24..502d9f0 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -163,6 +164,196 @@ namespace relational bool last_; }; + struct view_columns: object_columns_base, virtual context + { + typedef view_columns base; + + view_columns (): in_composite_ (false) {} + + virtual void + traverse_composite (semantics::data_member* pm, semantics::class_& c) + { + if (in_composite_) + { + object_columns_base::traverse_composite (pm, c); + return; + } + + // Override the column prerix. + // + semantics::data_member& m (*pm); + + // If we have literal column specified, use that. + // + if (m.count ("column")) + { + table_column const& tc (m.get ("column")); + + if (!tc.table.empty ()) + table_prefix_ = tc.table; + + column_prefix_ = object_columns_base::column_prefix (m); + } + // Otherwise, see if there is a column expression. For composite + // members in a view, this should be a single reference. + // + else if (m.count ("column-expr")) + { + column_expr const& e (m.get ("column-expr")); + + if (e.size () > 1) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: column expression specified for a data member " + << "of a composite value type" << endl; + + throw generation_failed (); + } + + data_member_path const& mp (e.back ().member_path); + + if (mp.size () > 1) + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: invalid data member in db pragma column" + << endl; + + throw generation_failed (); + } + + table_prefix_ = e.back ().table; + column_prefix_ = object_columns_base::column_prefix (*mp.back ()); + } + else + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: no column prefix provided for a view data member" + << endl; + + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": info: use db pragma column to specify the column prefix" + << endl; + + throw generation_failed (); + } + + in_composite_ = true; + object_columns_base::traverse_composite (pm, c); + in_composite_ = false; + } + + virtual bool + traverse_column (semantics::data_member& m, + string const& name, + bool first) + { + if (!first) + { + line_ += ','; + os << strlit (line_) << endl; + } + + line_.clear (); + + string col; + + // If we are inside a composite value, use the standard + // column name machinery. + // + if (in_composite_) + { + if (!table_prefix_.empty ()) + { + col += quote_id (table_prefix_); + col += '.'; + } + + col += quote_id (name); + } + // If we have literal column specified, use that. + // + else if (m.count ("column")) + { + table_column const& tc (m.get ("column")); + + if (!tc.expr) + { + if (!tc.table.empty ()) + { + col += quote_id (tc.table); + col += '.'; + } + + col += quote_id (tc.column); + } + else + col += tc.column; + } + // Otherwise, see if there is a column expression. + // + else if (m.count ("column-expr")) + { + column_expr const& e (m.get ("column-expr")); + + for (column_expr::const_iterator i (e.begin ()); i != e.end (); ++i) + { + switch (i->kind) + { + case column_expr_part::literal: + { + col += i->value; + break; + } + case column_expr_part::reference: + { + col += quote_id (i->table); + col += '.'; + col += quote_id (column_name (i->member_path)); + break; + } + } + } + } + else + { + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": error: no column name provided for a view data member" + << endl; + + cerr << m.file () << ":" << m.line () << ":" << m.column () + << ": info: use db pragma column to specify the column name" + << endl; + + throw generation_failed (); + } + + column (m, col); + + return true; + } + + // The column argument is a qualified and quoted column or + // expression. + // + virtual void + column (semantics::data_member&, string const& column) + { + line_ += column; + } + + virtual void + flush () + { + if (!line_.empty ()) + os << strlit (line_) << endl; + } + + protected: + string line_; + bool in_composite_; + string table_prefix_; // Table corresponding to column_prefix_; + }; + struct object_joins: object_columns_base, virtual context { typedef object_joins base; @@ -527,7 +718,7 @@ namespace relational typedef container_traits base; container_traits (semantics::class_& c) - : object_members_base (true, true), c_ (c) + : object_members_base (true, true, false), c_ (c) { if (object (c)) scope_ = "access::object_traits< " + c.fq_name () + " >"; @@ -634,7 +825,7 @@ namespace relational eager_ptr = has_a (*cvt, test_eager_pointer); } - string name (prefix_ + public_name (m) + "_traits"); + string name (flat_prefix_ + public_name (m) + "_traits"); string scope (scope_ + "::" + name); os << "// " << m.name () << endl @@ -1578,16 +1769,16 @@ namespace relational typedef container_cache_members base; container_cache_members () - : object_members_base (true, false) + : object_members_base (true, false, false) { } virtual void traverse_container (semantics::data_member& m, semantics::type&) { - string traits (prefix_ + public_name (m) + "_traits"); + string traits (flat_prefix_ + public_name (m) + "_traits"); os << db << "::container_statements_impl< " << traits << " > " << - prefix_ << m.name () << ";"; + flat_prefix_ << m.name () << ";"; } }; @@ -1596,7 +1787,7 @@ namespace relational typedef container_cache_init_members base; container_cache_init_members () - : object_members_base (true, false), first_ (true) + : object_members_base (true, false, false), first_ (true) { } @@ -1613,7 +1804,7 @@ namespace relational os << "," << endl << " "; - os << prefix_ << m.name () << " (c)"; + os << flat_prefix_ << m.name () << " (c)"; } protected: @@ -1635,7 +1826,7 @@ namespace relational }; container_calls (call_type call) - : object_members_base (true, false), + : object_members_base (true, false, false), call_ (call), obj_prefix_ ("obj.") { @@ -1686,8 +1877,8 @@ namespace relational string const& name (m.name ()); string obj_name (obj_prefix_ + name); - string sts_name (prefix_ + name); - string traits (prefix_ + public_name (m) + "_traits"); + string sts_name (flat_prefix_ + name); + string traits (flat_prefix_ + public_name (m) + "_traits"); // If this is a wrapped container, then we need to "unwrap" it. // @@ -2792,8 +2983,8 @@ namespace relational view_query_statement_ctor_args (type&) { os << "sts.connection ()," << endl - << "query_statement + q.clause ()," << endl - << "q.parameters_binding ()," << endl + << "qs.clause ()," << endl + << "qs.parameters_binding ()," << endl << "imb"; } @@ -2810,6 +3001,42 @@ namespace relational view_extra (c); // + // Query. + // + + // query_type + // + if (c.count ("objects")) + { + view_objects& objs (c.get ("objects")); + + if (objs.size () > 1) + { + for (view_objects::const_iterator i (objs.begin ()); + i < objs.end (); + ++i) + { + if (!i->alias.empty () && i->alias != table_name (*i->object)) + os << "const char " << traits << "::query_columns::" << endl + << i->alias << "_alias_[] = " << strlit (i->alias) << ";" + << endl; + } + } + else + { + // For a single object view we generate a shortcut without + // an intermediate typedef. + // + view_object const& vo (objs[0]); + + if (!vo.alias.empty () && vo.alias != table_name (*vo.object)) + os << "const char " << traits << "::" << endl + << "query_alias[] = " << strlit (vo.alias) << ";" + << endl; + } + } + + // // Functions. // @@ -2848,22 +3075,503 @@ namespace relational // init (view, image) // os << "void " << traits << "::" << endl - << "init (view_type& o, const image_type& i)" + << "init (view_type& o, const image_type& i, database& db)" << "{" << "ODB_POTENTIALLY_UNUSED (o);" << "ODB_POTENTIALLY_UNUSED (i);" + << "ODB_POTENTIALLY_UNUSED (db);" << endl; names (c, init_value_member_names_); os << "}"; - // query_statement + // query_statement() // - os << "const char " << traits << "::query_statement[] =" << endl - << strlit (c.get ("query")) << endl - << strlit (" ") << ";" << endl - << endl; + view_query& vq (c.get ("query")); + + if (vq.kind != view_query::runtime) + { + os << traits << "::query_base_type" << endl + << traits << "::" << endl + << "query_statement (const query_base_type& q)" + << "{"; + + if (vq.kind == view_query::complete) + { + os << "query_base_type r (" << endl; + + bool ph (false); + + if (!vq.literal.empty ()) + { + // See if we have the '(?)' placeholder. + // + // @@ Ideally we would need to make sure we don't match + // this inside strings and quoted identifier. So the + // proper way to handle this would be to tokenize the + // statement using sql_lexer, once it is complete enough. + // + string::size_type p (vq.literal.find ("(?)")); + + if (p != string::npos) + { + ph = true; + os << strlit (string (vq.literal, 0, p + 1)) + << " + q + " + << strlit (string (vq.literal, p + 2)); + } + else + os << strlit (vq.literal); + } + else + // Output the pragma location for easier error tracking. + // + os << "// From " << + location_file (vq.loc).leaf () << ":" << + location_line (vq.loc) << ":" << + location_column (vq.loc) << endl + << translate_expression ( + c, vq.expr, vq.scope, vq.loc, "query", &ph).value; + + os << ");"; + + // If there was no placeholder, add the query condition + // at the end. + // + if (!ph) + os << "r += q.clause_prefix ();" + << "r += q;"; + } + else // vq.kind == view_query::condition + { + os << "query_base_type r (" << endl + << strlit ("SELECT ") << endl; + + // Generate select-list. + // + { + instance t; + t->traverse (c); + } + + os << ");" + << endl; + + // Generate from-list. + // + view_objects const& objs (c.get ("objects")); + + for (view_objects::const_iterator i (objs.begin ()); + i != objs.end (); + ++i) + { + string l; + + // First object. + // + if (i == objs.begin ()) + { + l = "FROM "; + l += table_qname (*i->object); + + if (!i->alias.empty ()) + { + l += " AS "; + l += quote_id (i->alias); + } + + os << "r += " << strlit (l) << ";" + << endl; + + continue; + } + + expression e ( + translate_expression ( + c, i->cond, i->scope, i->loc, "object")); + + // Literal expression. + // + if (e.kind == expression::literal) + { + l = "LEFT JOIN "; + l += table_qname (*i->object); + + if (!i->alias.empty ()) + { + l += " AS "; + l += quote_id (i->alias); + } + + l += " ON"; + + os << "r += " << strlit (l) << ";" + // Output the pragma location for easier error tracking. + // + << "// From " << + location_file (i->loc).leaf () << ":" << + location_line (i->loc) << ":" << + location_column (i->loc) << endl + << "r += " << e.value << ";" + << endl; + + continue; + } + + // We have an object relationship (pointer) for which we need + // to come up with the corresponding JOIN condition. If this + // is a to-many relationship, then we first need to JOIN the + // container table. This code is similar to object_joins. + // + using semantics::data_member; + + data_member& m (*e.member_path.back ()); + + // Resolve the pointed-to object to view_object and do + // some sanity checks while at it. + // + semantics::class_* c (0); + + if (semantics::type* cont = container_wrapper (m.type ())) + c = object_pointer (container_vt (*cont)); + else + c = object_pointer (m.type ()); + + view_object const* vo (0); + + // Check if the pointed-to object has been previously + // associated with this view and is unambiguous. A + // pointer to ourselves is always assumed to point + // to this association. + // + if (i->object == c) + vo = &*i; + else + { + bool ambig (false); + + for (view_objects::const_iterator j (objs.begin ()); + j != i; + ++j) + { + if (j->object != c) + continue; + + if (vo == 0) + { + vo = &*j; + continue; + } + + // If it is the first ambiguous object, issue the + // error. + // + if (!ambig) + { + error (i->loc) + << "pointed-to object '" << c->name () << "' is " + << "ambiguous" << endl; + + info (i->loc) + << "candidates are:" << endl; + + info (vo->loc) + << " '" << vo->name () << "'" << endl; + + ambig = true; + } + + info (j->loc) + << " '" << j->name () << "'" << endl; + } + + if (ambig) + { + info (i->loc) + << "use the full join condition clause in db pragma " + << "object to resolve this ambiguity" << endl; + + throw generation_failed (); + } + + if (vo == 0) + { + error (i->loc) + << "pointed-to object '" << c->name () << "' " + << "specified in the join condition has not been " + << "previously associated with this view" << endl; + + throw generation_failed (); + } + } + + // Left and right-hand side table names. + // + string lt (e.vo->alias.empty () + ? table_name (*e.vo->object) + : e.vo->alias); + + string rt (vo->alias.empty () + ? table_name (*vo->object) + : vo->alias); + + // First join the container table if necessary. + // + data_member* im (inverse (m)); + + semantics::type* cont ( + container_wrapper (im != 0 ? im->type () : m.type ())); + + // Container table. + // + string ct; + if (cont != 0) + { + if (im != 0) + { + // For now a direct member can only be directly in + // the object scope. When this changes, the inverse() + // function would have to return a member path instead + // of just a single member. + // + table_prefix tp (table_name (*vo->object) + "_", 1); + ct = table_qname (*im, tp); + } + else + ct = table_qname (*e.vo->object, e.member_path); + } + + if (cont != 0) + { + l = "LEFT JOIN "; + l += ct; + l += " ON"; + os << "r += " << strlit (l) << ";"; + + // If we are the pointed-to object, then we have to turn + // things around. This is necessary to have the proper + // JOIN order. There seems to be a pattern there but + // it is not yet intuitively clear what it means. + // + if (im != 0) + { + if (i->object == c) + { + // container.value = pointer.id + // + l = ct; + l += '.'; + l += column_qname (*im, "value", "value"); + l += "="; + l += quote_id (lt); + l += '.'; + l += column_qname (*id_member (*e.vo->object)); + } + else + { + // container.id = pointed-to.id + // + l = ct; + l += '.'; + l += column_qname (*im, "id", "object_id"); + l += "="; + l += quote_id (rt); + l += '.'; + l += column_qname (*id_member (*vo->object)); + } + } + else + { + if (i->object == c) + { + // container.id = pointer.id + // + l = ct; + l += '.'; + l += column_qname (m, "id", "object_id"); + l += "="; + l += quote_id (lt); + l += '.'; + l += column_qname (*id_member (*e.vo->object)); + } + else + { + // container.value = pointed-to.id + // + l = ct; + l += '.'; + l += column_qname (m, "value", "value"); + l += "="; + l += quote_id (rt); + l += '.'; + l += column_qname (*id_member (*vo->object)); + } + } + + os << "r += " << strlit (l) << ";"; + } + + l = "LEFT JOIN "; + l += table_qname (*i->object); + + if (!i->alias.empty ()) + { + l += " AS "; + l += quote_id (i->alias); + } + + l += " ON"; + os << "r += " << strlit (l) << ";"; + + if (cont != 0) + { + if (im != 0) + { + if (i->object == c) + { + // container.id = pointed-to.id + // + l = ct; + l += '.'; + l += column_qname (*im, "id", "object_id"); + l += "="; + l += quote_id (rt); + l += '.'; + l += column_qname (*id_member (*vo->object)); + } + else + { + // container.value = pointer.id + // + l = ct; + l += '.'; + l += column_qname (*im, "value", "value"); + l += "="; + l += quote_id (lt); + l += '.'; + l += column_qname (*id_member (*e.vo->object)); + } + } + else + { + if (i->object == c) + { + // container.value = pointed-to.id + // + l = ct; + l += '.'; + l += column_qname (m, "value", "value"); + l += "="; + l += quote_id (rt); + l += '.'; + l += column_qname (*id_member (*vo->object)); + } + else + { + // container.id = pointer.id + // + l = ct; + l += '.'; + l += column_qname (m, "id", "object_id"); + l += "="; + l += quote_id (lt); + l += '.'; + l += column_qname (*id_member (*e.vo->object)); + } + } + } + else + { + if (im != 0) + { + // our.id = pointed-to.pointer + // + l = quote_id (lt); + l += '.'; + l += column_qname (*id_member (*e.vo->object)); + l += " = "; + l += quote_id (rt); + l += '.'; + l += column_qname (*im); + } + else + { + // our.pointer = pointed-to.id + // + l = quote_id (lt); + l += '.'; + l += column_qname (e.member_path); + l += " = "; + l += quote_id (rt); + l += '.'; + l += column_qname (*id_member (*vo->object)); + } + } + + os << "r += " << strlit (l) << ";" + << endl; + } + + // Generate the query condition. + // + if (!vq.literal.empty () || !vq.expr.empty ()) + { + os << "query_base_type c (" << endl; + + bool ph (false); + + if (!vq.literal.empty ()) + { + // See if we have the '(?)' placeholder. + // + // @@ Ideally we would need to make sure we don't match + // this inside strings and quoted identifier. So the + // proper way to handle this would be to tokenize the + // statement using sql_lexer, once it is complete enough. + // + string::size_type p (vq.literal.find ("(?)")); + + if (p != string::npos) + { + ph = true; + os << strlit (string (vq.literal, 0, p + 1)) + << " + q + " + << strlit (string (vq.literal, p + 2)); + } + else + os << strlit (vq.literal); + } + else + // Output the pragma location for easier error tracking. + // + os << "// From " << + location_file (vq.loc).leaf () << ":" << + location_line (vq.loc) << ":" << + location_column (vq.loc) << endl + << translate_expression ( + c, vq.expr, vq.scope, vq.loc, "query", &ph).value; + + os << ");"; + + if (!ph) + os << "c += q;"; + + os << "r += c.clause_prefix ();" + << "r += c;" + << endl; + } + else + { + os << "r += q.clause_prefix ();" + << "r += q;" + << endl; + } + } + + os << "return r;" + << "}"; + } // query () // @@ -2889,8 +3597,14 @@ namespace relational << "bind (imb.bind, im);" << "sts.image_version (im.version);" << "imb.version++;" - << "}" - << "shared_ptr st (" << endl + << "}"; + + if (vq.kind == view_query::runtime) + os << "query_base_type const& qs (q);"; + else + os << "query_base_type const& qs (query_statement (q));"; + + os << "shared_ptr st (" << endl << "new (shared) select_statement (" << endl; view_query_statement_ctor_args (c); @@ -2905,12 +3619,33 @@ namespace relational "class_view> > r (" << endl << "new (shared) " << db << "::result_impl (" << endl - << "q, st, sts));" + << "qs, st, sts));" << endl << "return result (r);" << "}"; } + struct expression + { + explicit + expression (std::string const& v): kind (literal), value (v) {} + expression (view_object* vo): kind (pointer), vo (vo) {} + + enum kind_type {literal, pointer}; + + kind_type kind; + std::string value; + data_member_path member_path; + view_object* vo; + }; + + expression + translate_expression (type& c, + cxx_tokens const&, + tree scope, + location_t loc, + string const& prag, + bool* placeholder = 0); // // composite // diff --git a/odb/relational/type-processor.cxx b/odb/relational/type-processor.cxx index a74af9b..650294d 100644 --- a/odb/relational/type-processor.cxx +++ b/odb/relational/type-processor.cxx @@ -7,7 +7,10 @@ #include +#include +#include #include +#include #include #include @@ -1079,6 +1082,449 @@ namespace relational tree container_traits_; }; + // + // + struct view_data_member: traversal::data_member, context + { + view_data_member (semantics::class_& c) + : view_ (c), + query_ (c.get ("query")), + amap_ (c.get ("alias-map")), + omap_ (c.get ("object-map")) + { + } + + struct assoc_member + { + semantics::data_member* m; + view_object* vo; + }; + + typedef vector assoc_members; + + virtual void + traverse (semantics::data_member& m) + { + using semantics::data_member; + + if (transient (m)) + 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 ("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")); + + 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 + { + if (i->kind != column_expr_part::reference) + continue; + + lex_.start (i->value); + + string t; + cpp_ttype tt (lex_.next (t)); + + string name; + tree decl (0); + semantics::class_* obj (0); + + // Check if this is an alias. + // + if (tt == CPP_NAME) + { + view_alias_map::iterator j (amap_.find (t)); + + if (j != amap_.end ()) + { + i->table = j->first; + obj = j->second->object; + + // Skip '::'. + // + if (lex_.next (t) != CPP_SCOPE) + { + error (i->loc) + << "member name expected after an alias in db pragma " + << "column" << endl; + throw generation_failed (); + } + + tt = lex_.next (t); + + cpp_ttype ptt; // Not used. + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex_, obj->tree_node (), name, false); + } + } + + // If it is not an alias, do the normal lookup. + // + if (obj == 0) + { + // Also get the object type. We need to do it so that + // we can get the correct (derived) table name (the + // member can come from a base class). + // + tree type; + cpp_ttype ptt; // Not used. + decl = lookup::resolve_scoped_name ( + t, tt, ptt, lex_, i->scope, name, false, &type); + + type = TYPE_MAIN_VARIANT (type); + + view_object_map::iterator j (omap_.find (type)); + + 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 generation_failed (); + } + + obj = j->second->object; + i->table = table_name (*obj); + } + + // Check that we have a data member. + // + if (TREE_CODE (decl) != FIELD_DECL) + { + error (i->loc) << "name '" << name << "' in db pragma column " + << "does not refer to a data member" << endl; + throw generation_failed (); + } + + data_member* m (dynamic_cast (unit.find (decl))); + i->member_path.push_back (m); + + // Finally, resolve nested members if any. + // + for (; tt == CPP_DOT; tt = lex_.next (t)) + { + lex_.next (t); // Get CPP_NAME. + + tree type (TYPE_MAIN_VARIANT (TREE_TYPE (decl))); + + decl = lookup_qualified_name ( + type, get_identifier (t.c_str ()), false, false); + + if (decl == error_mark_node || TREE_CODE (decl) != FIELD_DECL) + { + error (i->loc) << "name '" << t << "' in db pragma column " + << "does not refer to a data member" << endl; + throw generation_failed (); + } + + m = dynamic_cast (unit.find (decl)); + 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 generation_failed (); + } + catch (lookup::unable_to_resolve const& e) + { + error (i->loc) << "unable to resolve name '" << e.name () + << "' in db pragma column" << endl; + throw generation_failed (); + } + } + + // We have the source member, 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 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 && + !member_resolver::check_types (m.type (), src_m->type ())) + { + 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 generting 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 ("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) + 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 generation_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 generation_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; + ep.table = am.vo->alias.empty () + ? table_name (*am.vo->object) + : 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. + // + if (src_m != 0 && !m.count ("type") && src_m->count ("type")) + m.set ("column-type", src_m->get ("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; + traverse (*vo.object); + } + + virtual void + traverse (type& c) + { + if (!object (c)) + return; // Ignore transient bases. + + names (c); + inherits (c); + } + + public: + static bool + check_types (semantics::type& t1, semantics::type& t2) + { + using semantics::type; + using semantics::derived_type; + + // Require that the types be the same sans the wrapping and + // cvr-qualification. + // + type* pt1 (&t1); + type* pt2 (&t2); + + if (type* wt1 = context::wrapper (*pt1)) + pt1 = wt1; + + if (type* wt2 = context::wrapper (*pt2)) + pt2 = wt2; + + if (derived_type* dt1 = dynamic_cast (pt1)) + pt1 = &dt1->base_type (); + + if (derived_type* dt2 = dynamic_cast (pt2)) + pt2 = &dt2->base_type (); + + if (pt1 != pt2) + 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_ (m.type ()) + { + } + + virtual void + traverse (type& m) + { + // First see if we have the exact match. + // + if (name_ == m.name ()) + { + if (check (m)) + { + assoc_member am; + am.m = &m; + am.vo = vo_; + members_.push_back (am); + } + + return; + } + + // Don't bother with public name matching if we already + // have an exact match. + // + if (members_.empty ()) + { + if (pub_name_ == context::current ().public_name (m)) + { + if (check (m)) + { + assoc_member am; + am.m = &m; + am.vo = vo_; + pub_members_.push_back (am); + } + + return; + } + } + } + + bool + check (semantics::data_member& m) + { + // Make sure that the found node can possibly match. + // + if (context::transient (m) || context::inverse (m)) + return false; + + return check_types (m.type (), type_); + } + + assoc_members& members_; + assoc_members& pub_members_; + + string name_; + string pub_name_; + semantics::type& type_; + + view_object* vo_; + }; + + 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_ () @@ -1107,28 +1553,104 @@ namespace relational traverse_view (c); } + // + // View. + // + + struct relationship + { + semantics::data_member* member; + string name; + view_object* pointer; + view_object* pointee; + }; + + typedef vector 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 ("query") + : c.set ("query", view_query ())); + if (has_q) + { + if (!vq.literal.empty ()) + { + string q (upcase (vq.literal)); + vq.kind = (q.compare (0, 7, "SELECT ") == 0) + ? view_query::complete + : 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", then + // we have a complete query. + // + if (vq.expr.front ().type == CPP_STRING) + { + string q (upcase (vq.expr.front ().literal)); + vq.kind = (q.compare (0, 7, "SELECT ") == 0 || q == "SELECT") + ? view_query::complete + : view_query::condition; + } + else + vq.kind = view_query::condition; + } + else + vq.kind = view_query::runtime; + } + else + vq.kind = has_o ? view_query::condition : view_query::runtime; + + // 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 '" << c.fq_name () << "' 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 generation_failed (); + } + // Resolve referenced objects from tree nodes to semantic graph // nodes. // - if (c.count ("objects")) + view_alias_map& amap (c.set ("alias-map", view_alias_map ())); + view_object_map& omap (c.set ("object-map", view_object_map ())); + + if (has_o) { using semantics::class_; - typedef vector objects; - objects& objs (c.get ("objects")); + view_objects& objs (c.get ("objects")); - for (objects::iterator i (objs.begin ()); i < objs.end (); ++i) + for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i) { tree n (TYPE_MAIN_VARIANT (i->node)); if (TREE_CODE (n) != RECORD_TYPE) { - os << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: name '" << i->name << "' in db pragma object " - << " does not name a class" << endl; + error (i->loc) + << "name '" << i->orig_name << "' in db pragma object does " + << "not name a class" << endl; throw generation_failed (); } @@ -1137,22 +1659,242 @@ namespace relational if (!object (o)) { - os << c.file () << ":" << c.line () << ":" << c.column () << ":" - << " error: name '" << i->name << "' in db pragma object " - << "does not name a persistent class" << endl; + error (i->loc) + << "name '" << i->orig_name << "' in db pragma object does " + << "not name a persistent class" << endl; - os << o.file () << ":" << o.line () << ":" << o.column () << ":" - << " info: class '" << i->name << "' is defined here" - << endl; + info (o.file (), o.line (), o.column ()) + << "class '" << i->orig_name << "' is defined here" << endl; throw generation_failed (); } i->object = &o; + + if (i->alias.empty ()) + { + if (!omap.insert (view_object_map::value_type (n, &*i)).second) + { + error (i->loc) + << "persistent class '" << i->orig_name << "' is used in " + << "the view more than once" << endl; + + info (i->loc) + << "use the alias clause to assign it a different name" + << endl; + + throw generation_failed (); + } + } + else + { + if (!amap.insert ( + view_alias_map::value_type (i->alias, &*i)).second) + { + error (i->loc) + << "alias '" << i->alias << "' is used in the view more " + << "than once" << endl; + + throw generation_failed (); + } + } + + // 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. + // + if (vq.kind == view_query::condition && + i->cond.empty () && + i != objs.begin ()) + { + 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) + { + // 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. Ignore self-references if any, + // since they were already added to the list in the + // previous pass. + // + { + 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 generation_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 generation_failed (); + } + + // Synthesize the condition. + // + relationship const& r (rs.back ()); + + string name (r.pointer->alias.empty () + ? r.pointer->object->fq_name () + : r.pointer->alias); + name += "::"; + name += r.name; + + lexer.start (name); + + string t; + for (cpp_ttype tt (lexer.next (t)); + tt != CPP_EOF; + tt = lexer.next (t)) + { + cxx_token ct; + ct.type = tt; + ct.literal = t; + i->cond.push_back (ct); + } + } } } + + // 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 self_pointer) + : object_members_base (false, false, true), + relationships_ (rs), + self_pointer_ (self_pointer), + pointer_ (0), + pointee_ (pointee) + { + } + + void + traverse (view_object& pointer) + { + pointer_ = &pointer; + object_members_base::traverse (*pointer.object); + } + + virtual void + traverse_simple (semantics::data_member& m) + { + if (semantics::class_* c = object_pointer (m.type ())) + { + // 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. + // + if (inverse (m)) + return; + + // Ignore self-pointers if requested. + // + if (!self_pointer_ && pointer_->object == c) + return; + + if (pointee_.object == 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& t) + { + if (semantics::class_* c = + object_pointer (context::container_vt (t))) + { + if (inverse (m, "value")) + return; + + // Ignore self-pointers if requested. + // + if (!self_pointer_ && pointer_->object == c) + return; + + if (pointee_.object == 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_; + }; + void assign_pointer (type& c) { @@ -1301,7 +2043,7 @@ namespace relational bool punc (false); bool scoped (false); - for (cpp_ttype tt = lexer.next (t); + for (cpp_ttype tt (lexer.next (t)); tt != CPP_EOF; tt = lexer.next (t)) { @@ -1479,7 +2221,7 @@ namespace relational } private: - cxx_lexer lexer; + cxx_string_lexer lexer; data_member member_; traversal::names member_names_; diff --git a/odb/semantics/elements.cxx b/odb/semantics/elements.cxx index d6ff1c7..f503adb 100644 --- a/odb/semantics/elements.cxx +++ b/odb/semantics/elements.cxx @@ -146,7 +146,7 @@ namespace semantics // @@ Creating a lexer for each call is a bad idea. Need // to cache it somewhere. // - cxx_lexer l; + cxx_string_lexer l; l.start (n); string r, t; diff --git a/odb/validator.cxx b/odb/validator.cxx index bf7784e..201caf1 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -187,8 +187,15 @@ namespace // struct class_: traversal::class_ { - class_ (bool& valid, semantics::unit& unit, value_type& vt) - : valid_ (valid), unit_ (unit), vt_ (vt), member_ (valid) + class_ (bool& valid, + options const& ops, + semantics::unit& unit, + value_type& vt) + : valid_ (valid), + options_ (ops), + unit_ (unit), + vt_ (vt), + member_ (valid) { *this >> names_ >> member_; } @@ -354,6 +361,22 @@ namespace virtual void traverse_view (type& c) { + // Views require query support. + // + if (!options_.generate_query ()) + { + cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " error: query support is required when using views" + << endl; + + cerr << c.file () << ":" << c.line () << ":" << c.column () << ":" + << " info: use the --generate-query option to enable query " + << "support" + << endl; + + valid_ = false; + } + // Check bases. // for (type::inherits_iterator i (c.inherits_begin ()); @@ -485,6 +508,7 @@ namespace } bool& valid_; + options const& options_; semantics::unit& unit_; value_type& vt_; @@ -494,7 +518,7 @@ namespace } bool validator:: -validate (options const&, +validate (options const& ops, semantics::unit& u, semantics::path const&) { @@ -505,7 +529,7 @@ validate (options const&, traversal::declares unit_declares; traversal::namespace_ ns; value_type vt (valid); - class_ c (valid, u, vt); + class_ c (valid, ops, u, vt); unit >> unit_defines >> ns; unit_defines >> c; -- cgit v1.1