aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2011-09-16 16:03:25 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2011-09-16 16:03:25 +0200
commitb79567fbc72df23f870049652d5f254aba948bea (patch)
tree186168269cf249ce97be89fd02aab4c75e83574c
parentd780414989ef7e101cdaf269d4b01003d0721e6a (diff)
Support for views; integrated part
-rw-r--r--odb/common.cxx159
-rw-r--r--odb/common.hxx48
-rw-r--r--odb/context.cxx63
-rw-r--r--odb/context.hxx117
-rw-r--r--odb/cxx-lexer.cxx110
-rw-r--r--odb/cxx-lexer.hxx79
-rw-r--r--odb/cxx-token.hxx20
-rw-r--r--odb/error.cxx78
-rw-r--r--odb/error.hxx41
-rw-r--r--odb/gcc-fwd.hxx3
-rw-r--r--odb/lookup.cxx129
-rw-r--r--odb/lookup.hxx54
-rw-r--r--odb/makefile1
-rw-r--r--odb/pragma.cxx528
-rw-r--r--odb/relational/context.hxx12
-rw-r--r--odb/relational/header.hxx121
-rw-r--r--odb/relational/inline.hxx53
-rw-r--r--odb/relational/mysql/source.cxx26
-rw-r--r--odb/relational/pgsql/schema.cxx2
-rw-r--r--odb/relational/pgsql/source.cxx11
-rw-r--r--odb/relational/schema.hxx8
-rw-r--r--odb/relational/source.cxx544
-rw-r--r--odb/relational/source.hxx777
-rw-r--r--odb/relational/type-processor.cxx772
-rw-r--r--odb/semantics/elements.cxx2
-rw-r--r--odb/validator.cxx32
26 files changed, 3515 insertions, 275 deletions
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<string> ("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<string> ("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 <odb/context.hxx>
-// 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 <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <cctype> // std::toupper
#include <cassert>
#include <odb/context.hxx>
@@ -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<string> ("table");
+ if (c.count ("table"))
+ name += c.get<string> ("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<string> ("column");
- else if (m.type ().count ("column"))
- return m.type ().get<string> ("column");
+ return m.get<table_column> ("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<string> (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 <cutl/shared-ptr.hxx>
#include <odb/options.hxx>
+#include <odb/cxx-token.hxx>
#include <odb/semantics.hxx>
#include <odb/traversal.hxx>
@@ -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<semantics::data_member*> 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_object> view_objects;
+
+typedef std::map<std::string, view_object*> view_alias_map;
+typedef std::map<tree, view_object*> 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<column_expr_part>
+{
+ 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<cpp_ttype> (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 <string>
+#include <odb/cxx-token.hxx>
+
// 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 <boris@codesynthesis.com>
+// 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 <string>
+#include <vector>
+
+struct cxx_token
+{
+ unsigned int type; // Untyped cpp_ttype.
+ std::string literal; // Only used for name, string, number, etc.
+};
+
+typedef std::vector<cxx_token> 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 <odb/gcc.hxx>
#include <odb/error.hxx>
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 <odb/gcc.hxx>
+#include <odb/gcc-fwd.hxx>
+#include <cstddef>
#include <iostream>
#include <cutl/fs/path.hxx>
@@ -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 <coretypes.h>
+
+typedef unsigned int source_location; // <line-map.h>
+typedef source_location location_t; // <input.h>
}
#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 <boris@codesynthesis.com>
+// copyright : Copyright (c) 2009-2011 Code Synthesis Tools CC
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/lookup.hxx>
+
+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 <boris@codesynthesis.com>
+// 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 <odb/gcc.hxx>
+
+#include <string>
+
+#include <odb/cxx-lexer.hxx>
+
+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 <odb/gcc.hxx>
+#include <cctype> // std::isalnum
#include <vector>
+#include <sstream>
#include <odb/error.hxx>
+#include <odb/lookup.hxx>
#include <odb/pragma.hxx>
+#include <odb/cxx-token.hxx>
#include <odb/cxx-lexer.hxx>
#include <odb/context.hxx>
@@ -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<view_object>;
@@ -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 ("<name>")
+ // column ("<name>.<name>")
+ // column ("<name>"."<name>")
+ // 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)
+ {
+ // "<name>" or "<name>.<name>"
+ //
+ 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)
+ {
+ // "<name>"."<name>"
+ //
+ 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 ("<name>")
// value_column ("<name>")
// index_column ("<name>")
// key_column ("<name>")
@@ -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<semantics::class_*> objects;
-
- objects const& objs (c.get<objects> ("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<view_objects> ("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<view_query> ("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> 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> 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<tables>& 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<tables>& 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 <odb/gcc.hxx>
+
+#include <odb/lookup.hxx>
+#include <odb/cxx-lexer.hxx>
+
#include <odb/relational/source.hxx>
#include <odb/relational/generate.hxx>
@@ -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<data_member*> (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<data_member*> (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<view_alias_map> ("alias-map"));
+ view_object_map const& omap (c.get<view_object_map> ("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 <vector>
#include <sstream>
+#include <odb/error.hxx>
#include <odb/emitter.hxx>
#include <odb/relational/context.hxx>
@@ -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<table_column> ("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> ("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<table_column> ("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> ("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<view_objects> ("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<string> ("query")) << endl
- << strlit (" ") << ";" << endl
- << endl;
+ view_query& vq (c.get<view_query> ("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<view_columns> t;
+ t->traverse (c);
+ }
+
+ os << ");"
+ << endl;
+
+ // Generate from-list.
+ //
+ view_objects const& objs (c.get<view_objects> ("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<select_statement> 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<select_statement> 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<view_type, class_view> (" << endl
- << "q, st, sts));"
+ << "qs, st, sts));"
<< endl
<< "return result<view_type> (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 <vector>
+#include <odb/error.hxx>
+#include <odb/lookup.hxx>
#include <odb/cxx-lexer.hxx>
+#include <odb/common.hxx>
#include <odb/relational/context.hxx>
#include <odb/relational/type-processor.hxx>
@@ -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<view_query> ("query")),
+ amap_ (c.get<view_alias_map> ("alias-map")),
+ omap_ (c.get<view_object_map> ("object-map"))
+ {
+ }
+
+ struct assoc_member
+ {
+ semantics::data_member* m;
+ view_object* vo;
+ };
+
+ typedef vector<assoc_member> assoc_members;
+
+ virtual void
+ traverse (semantics::data_member& m)
+ {
+ using semantics::data_member;
+
+ if (transient (m))
+ return;
+
+ data_member* src_m (0); // Source member.
+
+ // Resolve member references in column expressions.
+ //
+ if (m.count ("column"))
+ {
+ // Column literal.
+ //
+ if (query_.kind != view_query::condition)
+ {
+ warn (m.get<location_t> ("column-location"))
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ }
+
+ return;
+ }
+ else if (m.count ("column-expr"))
+ {
+ column_expr& e (m.get<column_expr> ("column-expr"));
+
+ if (query_.kind != view_query::condition)
+ {
+ warn (e.loc)
+ << "db pragma column ignored in a view with "
+ << (query_.kind == view_query::runtime ? "runtime" : "complete")
+ << " query" << endl;
+ return;
+ }
+
+ for (column_expr::iterator i (e.begin ()); i != e.end (); ++i)
+ {
+ // This code is quite similar to translate_expression in the
+ // source generator.
+ //
+ try
+ {
+ 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<data_member*> (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<data_member*> (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<view_objects> ("objects"));
+
+ assoc_members exact_members, pub_members;
+ member_resolver resolver (exact_members, pub_members, m);
+
+ for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
+ 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<string> ("column-type"));
+
+ // Check the return statements above if you add any extra logic
+ // here.
+ }
+
+ struct member_resolver: traversal::class_
+ {
+ member_resolver (assoc_members& members,
+ assoc_members& pub_members,
+ semantics::data_member& m)
+ : member_ (members, pub_members, m)
+ {
+ *this >> names_ >> member_;
+ *this >> inherits_ >> *this;
+ }
+
+ void
+ traverse (view_object& vo)
+ {
+ member_.vo_ = &vo;
+ 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<derived_type*> (pt1))
+ pt1 = &dt1->base_type ();
+
+ if (derived_type* dt2 = dynamic_cast<derived_type*> (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<relationship> relationships;
+
virtual void
traverse_view (type& c)
{
+ bool has_q (c.count ("query"));
+ bool has_o (c.count ("objects"));
+
+ // Determine the kind of query template we've got.
+ //
+ view_query& vq (has_q
+ ? c.get<view_query> ("query")
+ : c.set ("query", view_query ()));
+ if (has_q)
+ {
+ if (!vq.literal.empty ())
+ {
+ string q (upcase (vq.literal));
+ 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<view_object> objects;
- objects& objs (c.get<objects> ("objects"));
+ view_objects& objs (c.get<view_objects> ("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;