summaryrefslogtreecommitdiff
path: root/odb/relational
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 /odb/relational
parentd780414989ef7e101cdaf269d4b01003d0721e6a (diff)
Support for views; integrated part
Diffstat (limited to 'odb/relational')
-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
10 files changed, 2258 insertions, 68 deletions
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_;