aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--doc/manual.xhtml18
-rw-r--r--odb/context.hxx2
-rw-r--r--odb/pragma.cxx68
-rw-r--r--odb/relational/mysql/source.cxx13
-rw-r--r--odb/relational/processor.cxx15
-rw-r--r--odb/relational/source.cxx72
-rw-r--r--odb/relational/source.hxx17
-rw-r--r--odb/relational/sqlite/source.cxx19
9 files changed, 210 insertions, 19 deletions
diff --git a/NEWS b/NEWS
index f003c4b..896c161 100644
--- a/NEWS
+++ b/NEWS
@@ -27,6 +27,11 @@ Version 2.4.0
Section 15.2, "Persistent Class Template Instantiations" in the ODB
manual.
+ * Support for object and table join types in views. Supported join type
+ are left, right, full, inner, and cross with left being the default.
+ For details, refer to Sections 10.1, "Object Views" and 10.3, "Table
+ Views" in the ODB manual.
+
* Support for result modifiers in view query conditions. Currently
supported result modifiers are 'distinct' (which is translated to
SELECT DISTINCT) and 'for_update' (which is translated to FOR UPDATE or
diff --git a/doc/manual.xhtml b/doc/manual.xhtml
index 3e941dc..fce5302 100644
--- a/doc/manual.xhtml
+++ b/doc/manual.xhtml
@@ -9953,6 +9953,7 @@ struct employee_employer
<p><code><b>object(</b><i>name</i>
[<b>=</b> <i>alias</i>]
+ [<i>join-type</i>]
[<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
<p>The <i>name</i> part is a potentially qualified persistent class
@@ -9960,12 +9961,16 @@ struct employee_employer
part gives this object an alias. If provided, the alias is used
in several contexts instead of the object's unqualified name. We
will discuss aliases further as we cover each of these contexts
- below. The optional <i>join-condition</i> part provides the
+ below. The optional <i>join-type</i> part specifies the way this
+ object is associated. It can be <code>left</code>, <code>right</code>,
+ <code>full</code>, <code>inner</code>, and <code>cross</code>
+ with <code>left</code> being the default.
+ Finally, the optional <i>join-condition</i> part provides the
criteria which should be used to associate this object with any
of the previously associated objects or, as we will see in
<a href="#10.4">Section 10.4, "Mixed Views"</a>, tables. Note that
while the first associated object can have an alias, it cannot
- have a join condition.</p>
+ have a join type or condition.</p>
<p>For each subsequent associated object the ODB compiler needs
a join condition and there are several ways to specify
@@ -10784,6 +10789,7 @@ struct employee_max_vacation
<p><code><b>table("</b><i>name</i><b>"</b>
[<b>=</b> <b>"</b><i>alias</i><b>"</b>]
+ [<i>join-type</i>]
[<b>:</b> <i>join-condition</i>]<b>)</b></code></p>
<p>The <i>name</i> part is a database table name. The optional
@@ -10791,12 +10797,16 @@ struct employee_max_vacation
alias must be used instead of the table whenever a reference
to a table is used. Contexts where such a reference may
be needed include the join condition (discussed below),
- column names, and query expressions. The optional <i>join-condition</i>
+ column names, and query expressions. The optional <i>join-type</i>
+ part specifies the way this table is associated. It can
+ be <code>left</code>, <code>right</code>, <code>full</code>,
+ <code>inner</code>, and <code>cross</code> with <code>left</code>
+ being the default. Finally, the optional <i>join-condition</i>
part provides the criteria which should be used to associate this
table with any of the previously associated tables or, as we will see in
<a href="#10.4">Section 10.4, "Mixed Views"</a>, objects. Note that
while the first associated table can have an alias, it cannot have
- a join condition.</p>
+ a join type or condition.</p>
<p>Similar to object views, for each subsequent associated table the
ODB compiler needs a join condition. However, unlike for object views,
diff --git a/odb/context.hxx b/odb/context.hxx
index d6e4713..c408063 100644
--- a/odb/context.hxx
+++ b/odb/context.hxx
@@ -159,8 +159,10 @@ struct view_object
name () const;
enum kind_type { object, table };
+ enum join_type { left, right, full, inner, cross };
kind_type kind;
+ join_type join;
tree obj_node; // Tree node if kind is object.
std::string obj_name; // Name as specified in the pragma if kind is object.
qname tbl_name; // Table name if kind is table.
diff --git a/odb/pragma.cxx b/odb/pragma.cxx
index 618df67..08dc7ae 100644
--- a/odb/pragma.cxx
+++ b/odb/pragma.cxx
@@ -1131,7 +1131,7 @@ handle_pragma (cxx_lexer& l,
else if (p == "table")
{
// table (<name>)
- // table (<name> [= "<alias>"] [: "<cond>"] (view only)
+ // table (<name> [= "<alias>"] [type] [: "<cond>"] (view only)
//
// <name> := "name" | "name.name" | "name"."name"
//
@@ -1183,9 +1183,41 @@ handle_pragma (cxx_lexer& l,
tt = l.next (tl, &tn);
}
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
if (tt == CPP_COLON)
{
// We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
tt = l.next (tl, &tn);
@@ -1653,7 +1685,7 @@ handle_pragma (cxx_lexer& l,
}
else if (p == "object")
{
- // object (fq-name [ = name] [: expr])
+ // object (fq-name [ = name] [type] [: expr])
//
// Make sure we've got the correct declaration type.
@@ -1706,9 +1738,41 @@ handle_pragma (cxx_lexer& l,
tt = l.next (tl, &tn);
}
+ if (tt == CPP_NAME)
+ {
+ // We have a JOIN type.
+ //
+ if (tl == "left")
+ vo.join = view_object::left;
+ else if (tl == "right")
+ vo.join = view_object::right;
+ else if (tl == "full")
+ vo.join = view_object::full;
+ else if (tl == "inner")
+ vo.join = view_object::inner;
+ else if (tl == "cross")
+ vo.join = view_object::cross;
+ else
+ {
+ error (l) << "unknown join type '" << tl << "'" << endl;
+ return;
+ }
+
+ tt = l.next (tl, &tn);
+ }
+ else
+ vo.join = view_object::left;
+
if (tt == CPP_COLON)
{
// We have a condition.
+ //
+ if (vo.join == view_object::cross)
+ {
+ error (l)
+ << "no join condition can be specified for a cross join" << endl;
+ return;
+ }
tt = l.next (tl, &tn);
diff --git a/odb/relational/mysql/source.cxx b/odb/relational/mysql/source.cxx
index bcc7f23..9725f18 100644
--- a/odb/relational/mysql/source.cxx
+++ b/odb/relational/mysql/source.cxx
@@ -681,6 +681,19 @@ namespace relational
{
os << im << "value = 0;";
}
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ if (vo.join == view_object::full)
+ {
+ error (vo.loc)
+ << "FULL OUTER JOIN is not supported by MySQL" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
};
entry<class_> class_entry_;
diff --git a/odb/relational/processor.cxx b/odb/relational/processor.cxx
index 16904b6..9442850 100644
--- a/odb/relational/processor.cxx
+++ b/odb/relational/processor.cxx
@@ -1344,6 +1344,15 @@ namespace relational
for (view_objects::iterator i (objs.begin ()); i != objs.end (); ++i)
{
+ if (i == objs.begin () && i->join != view_object::left)
+ {
+ error (i->loc)
+ << "no join type can be specified for the first associated "
+ << (i->kind == view_object::object ? "object" : "table")
+ << endl;
+ throw operation_failed ();
+ }
+
if (i->kind != view_object::object)
{
// Make sure we have join conditions for tables unless it
@@ -1362,11 +1371,13 @@ namespace relational
// 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.
+ // automatically based on object relationships. CROSS JOIN
+ // has no condition.
//
if (vq.kind == view_query::condition &&
i->cond.empty () &&
- i != objs.begin ())
+ i != objs.begin () &&
+ i->join != view_object::cross)
{
relationships rs;
diff --git a/odb/relational/source.cxx b/odb/relational/source.cxx
index dc083bb..e0fbab3 100644
--- a/odb/relational/source.cxx
+++ b/odb/relational/source.cxx
@@ -4776,12 +4776,19 @@ traverse_view (type& c)
continue;
}
- l = "LEFT JOIN ";
+ l = join_syntax (*i);
+ l += ' ';
l += quote_id (i->tbl_name);
if (!i->alias.empty ())
l += (need_alias_as ? " AS " : " ") + quote_id (i->alias);
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
semantics::scope& scope (
dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
@@ -4851,20 +4858,28 @@ traverse_view (type& c)
semantics::scope& scope (
dynamic_cast<semantics::scope&> (*unit.find (i->scope)));
- expression e (
- translate_expression (
- c, i->cond, scope, i->loc, "object"));
+ expression e (i->join == view_object::cross
+ ? expression ("") // Dummy literal expression.
+ : translate_expression (
+ c, i->cond, scope, i->loc, "object"));
// Literal expression.
//
if (e.kind == expression::literal)
{
- l = "LEFT JOIN ";
+ l = join_syntax (*i);
+ l += ' ';
l += table_qname (o);
if (!alias.empty ())
l += (need_alias_as ? " AS " : " ") + quote_id (alias);
+ if (i->join == view_object::cross) // No ON condition for CROSS JOIN.
+ {
+ from.push_back (l);
+ continue;
+ }
+
l += " ON";
// Add the pragma location for easier error tracking.
@@ -4891,6 +4906,9 @@ traverse_view (type& c)
// is a to-many relationship, then we first need to JOIN the
// container table. This code is similar to object_joins.
//
+ // Note that this cannot be CROSS JOIN; we've handled that case
+ // above.
+ //
using semantics::data_member;
data_member& m (*e.member_path.back ());
@@ -5029,7 +5047,8 @@ traverse_view (type& c)
string ct; // Container table.
if (cont != 0)
{
- l = "LEFT JOIN ";
+ l = join_syntax (*i);
+ l += ' ';
// The same relationship can be used by multiple associated
// objects. So if the object that contains this container has
@@ -5174,7 +5193,15 @@ traverse_view (type& c)
}
}
- l = "LEFT JOIN ";
+ // If we have already joined the container with the desired
+ // join type, then use LEFT JOIN to join the object to the
+ // container. This is the right thing to do since an entry
+ // in the container can only point (either via id or value)
+ // to a single object.
+ //
+ l = (cont == 0 ? join_syntax (*i) : "LEFT JOIN");
+ l += ' ';
+
l += table_qname (o);
if (!alias.empty ())
@@ -5563,13 +5590,36 @@ traverse_view (type& c)
{
if (i->compare (0, 5, "FROM ") == 0)
os << "r += " << strlit (*i) << ";";
- else if (i->compare (0, 5, "LEFT ") == 0)
- os << endl
- << "r += " << strlit (sep + *i) << ";";
else if (i->compare (0, 3, "// ") == 0)
os << *i << endl;
else
- os << "r += " << *i << ";"; // Already a string literal.
+ {
+ // See if this is a JOIN. The exact spelling is database-dependent,
+ // but we know there is the JOIN word in there somewhere and before
+ // it we should only have keywords and spaces.
+ //
+ size_t p (i->find ("JOIN "));
+ if (p != string::npos)
+ {
+ // Make sure before it we only have A-Z and spaces.
+ //
+ for (char c; p != 0; --p)
+ {
+ c = (*i)[p - 1];
+ if ((c < 'A' || c > 'Z') && c != ' ')
+ break;
+ }
+
+ if (p == 0)
+ os << endl
+ << "r += " << strlit (sep + *i) << ";";
+ }
+
+ // Something else, assume already a string literal.
+ //
+ if (p != 0)
+ os << "r += " << *i << ";";
+ }
}
// Generate the query condition.
diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx
index 48d0e1d..bf675f2 100644
--- a/odb/relational/source.hxx
+++ b/odb/relational/source.hxx
@@ -6686,6 +6686,23 @@ namespace relational
return c.get<view_query> ("query").for_update ? "FOR UPDATE" : "";
}
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* r;
+
+ switch (vo.join)
+ {
+ case view_object::left: r = "LEFT JOIN"; break;
+ case view_object::right: r = "RIGHT JOIN"; break;
+ case view_object::full: r = "FULL JOIN"; break;
+ case view_object::inner: r = "INNER JOIN"; break;
+ case view_object::cross: r = "CROSS JOIN"; break;
+ }
+
+ return r;
+ }
+
virtual void
traverse_view (type& c);
diff --git a/odb/relational/sqlite/source.cxx b/odb/relational/sqlite/source.cxx
index 0824eb2..0e842c1 100644
--- a/odb/relational/sqlite/source.cxx
+++ b/odb/relational/sqlite/source.cxx
@@ -255,6 +255,25 @@ namespace relational
//
return "";
}
+
+ virtual string
+ join_syntax (view_object const& vo)
+ {
+ const char* n (0);
+
+ if (vo.join == view_object::full)
+ n = "FULL OUTER JOIN";
+ else if (vo.join == view_object::right)
+ n = "RIGHT OUTER JOIN";
+
+ if (n != 0)
+ {
+ error (vo.loc) << n << " is not supported by SQLite" << endl;
+ throw operation_failed ();
+ }
+
+ return base::join_syntax (vo);
+ }
};
entry<class_> class_entry_;
}