History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: CAY-560
Type: New Feature New Feature
Status: Closed Closed
Resolution: Fixed
Priority: Major Major
Assignee: Mike Kienenberger
Reporter: Øyvind Harboe
Votes: 3
Watchers: 1
Operations

If you were logged in you would be able to see more operations.
Cayenne

Add support for outer joins

Created: 31/May/06 02:33 AM   Updated: 06/May/08 01:43 PM
Component/s: Cayenne Core Library
Affects Version/s: None
Fix Version/s: 3.0

File Attachments:
  Size
Text File Licensed for inclusion in ASF works 1.2-outerjoin-null-2006-09-05-01.txt 2006-09-05 03:28 PM Mike Kienenberger 134 kb
Text File Licensed for inclusion in ASF works cayenne-2.0-patch-outerjoin-null-audit-customexp.txt 2007-08-21 07:10 PM Mike Kienenberger 269 kb
Issue Links:
reference


 Description  « Hide
Currently outer joins are not supported.

See thread:

http://objectstyle.org/cayenne/lists/cayenne-user/2006/05/0165.html




 // This Java code will result in the SQL query immediately below...
SelectQuery query1=new SelectQuery(ElcRole.class);
query1.setQualifier(ExpressionFactory.matchExp(ElcRole.ELC_ROLEMEMBER_ARRAY_PROPERTY + "." + ElcRolemember.TO_SYS_USER_PROPERTY, getSysUser()).
  orExp(ExpressionFactory.matchExp(ElcRole.TO_SYS_USER_PROPERTY, getSysUser())));
List l=context.performQuery(query1);

Generates =>
 
SELECT DISTINCT t0.ELCROLE_NAME, t0.ELCROLE_ID, t0.SYSUSER_ID
FROM ELC_ROLE t0 LEFT OUTER JOIN
                      ELC_ROLEMEMBER t1 ON t0.ELCROLE_ID = t1.ELCROLE_ID
WHERE (t1.SYSUSER_ID = 'BD93F348-8C02-4742-BA97-2456E5CD3881') OR
                      (t0.SYSUSER_ID = 'BD93F348-8C02-4742-BA97-2456E5CD3881')

But should(?) have generated =>

SELECT DISTINCT t0.ELCROLE_NAME, t0.ELCROLE_ID, t0.SYSUSER_ID
FROM ELC_ROLE t0 LEFT OUTER JOIN
                      ELC_ROLEMEMBER t1 ON t0.ELCROLE_ID = t1.ELCROLE_ID
WHERE (t1.SYSUSER_ID = 'BD93F348-8C02-4742-BA97-2456E5CD3881') OR
                      (t0.SYSUSER_ID = 'BD93F348-8C02-4742-BA97-2456E5CD3881')
                      


 All   Comments   Change History   Subversion Commits      Sort Order: Ascending order - Click to sort in descending order
Mike Kienenberger - 05/Sep/06 03:28 PM
Patches to Cayenne 1.2 final to provide Outer Join support as well as inequality expression null handling.

Note that outer joins are best described using a "+" character after a non-mandatory relationship path component in an expression string.

Currently the parser support for such strings is poorly implemented. It was done by allowing "+" as a trailing symbol in an identifier name even though identifiers do not support + as a symbol.

   < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>)* (["+"])? >

There are probably some optimizations that should be performed so that any outer join expression between two outer-joined tables is used only once in a query. There may also be prefetching issues as well for outer join expressions, which again might be solved by using each outer join expression only once.

Mike Kienenberger - 07/Sep/06 12:09 PM
A note that ANSI join notation is available starting with 10.0.0 for OpenBase, but not before. It's also unclear if there's outer join support for OpenBase yet, but "INNER JOIN and ON" is supported.

Mike Kienenberger - 07/Sep/06 01:00 PM
Scott Keith <scott@openbase.com> wrote:
> We have already modified the server this week to fully
> support the ANSI SQL keywords "INNER JOIN" and "LEFT OUTER JOIN".

Scott verified that LEFT OUTER JOIN is also working and supported as of OpenBase 10.0.0.

Andrus Adamchik - 12/Jun/07 07:57 AM
Mike, do we still want to support join semantics in Expressions considering EJBQLQuery already supports them?

Mike Kienenberger - 12/Jun/07 08:31 AM
If we're going to continue supporting Expressions, I would think that would be a good idea. However, I don't know what the future of the original Cayenne expression language/query classes will be.

I've been using the above patch for a long time now, and couldn't move to something that didn't support outer joins.

Mike Kienenberger - 21/Aug/07 07:10 PM
Here's a rework of the patch for Cayenne 2.0.

Unfortunately, it's also including some of my other modifications like hooks for audit logging and support for raw sql trinary CustomExpressions.

I'll try to pull these bits out into a separate patch when I next get a chance, but what's here passes all unit tests (except for 3 CustomExpression tests that never passed in the first place).


Also, due to the following errors, I couldn't create a patch for the generated files:

==========================
diff --old E:/workspaces/cayenne/STABLE-2.0/cayenne-java
    Inconsistent line ending style
svn: File 'E:\workspaces\cayenne\STABLE-2.0\cayenne-java\src\cayenne\java\org\apache\cayenne\wocompat\parser\ParserTokenManager.java' has inconsistent newlines
svn: File 'E:\workspaces\cayenne\STABLE-2.0\cayenne-java\src\cayenne\java\org\apache\cayenne\exp\parser\ExpressionParserTokenManager.java' has inconsistent newlines
==========================

So you'll need to run the following cayenne-ant module ant tasks to regenerate them manually.

ant -f build.xml javacc
ant -f build.xml jjtree

Andrus Adamchik - 29/Apr/08 02:27 PM
Per CAY-929, I implemented a big chunk of Mike's patch (I couldn't use the patch directly - it is too loaded). The two things that are NOT included are:

* Expression.fromString() support for OUTER JOINS
* Split joins (I am still unclear on the logic here... we can't automatically guess where to split the joins, and where to combine them, some explicit API for that)..

Maybe we should close this Jira and handle the two tasks above as separate new Jiras?

Mike Kienenberger - 29/Apr/08 02:39 PM
Excellent!

Split joins were explicitly indicated using "+" in the path after a relationship segment. (I had thought it was "|", but reviewing the patch seems to indicate "+").


See the SelectTranslatorOuterJoinTst .java file from the 2.0 patch file for examples like the following:

+ public void testCreateSqlStringForPaintingTitleAndGroupArrayNameWithOuterJoinOracle() throws Exception {
+ // query with joint prefetches and other joins
+ Expression expression = Expression.fromString(
+ "paintingArray+.paintingTitle <> 'a' and groupArray+.name = 'b'");
+ SelectQuery q = new SelectQuery(Artist.class, expression);
+
+ Template test = new Template() {
+
+ void test(SelectTranslator transl) throws Exception {
+
+ String sqlString = transl.createSqlString();
+
+ assertEquals(
+ "SELECT DISTINCT "
+
+ + "t0.ARTIST_NAME, t0.DATE_OF_BIRTH, t0.ARTIST_ID "
+
+ + "FROM "
+
+ + "ARTIST t0 "
+ + "LEFT OUTER JOIN PAINTING t1 "
+ + "ON t0.ARTIST_ID = t1.ARTIST_ID "
+ + "LEFT OUTER JOIN ARTIST_GROUP t2 "
+ + "ON t0.ARTIST_ID = t2.ARTIST_ID "
+ + "LEFT OUTER JOIN ARTGROUP t3 "
+ + "ON t2.GROUP_ID = t3.GROUP_ID "
+
+ + "WHERE "
+
+ + "((t1.PAINTING_TITLE IS NULL) OR (t1.PAINTING_TITLE <> ?)) AND (t3.NAME = ?)",
+ sqlString);
+ }
+ };
+
+ test.testOracle(q);
+ }

Andrus Adamchik - 29/Apr/08 02:52 PM
Hmm... I am also using "+" for the OUTER joins.. Split stuff is whether we should generate one or multiple joins for multiple expressions that go over the same path... I thought that is separate and should be using some other notation?

Mike Kienenberger - 29/Apr/08 03:31 PM
Hmm. You might be right. I honestly don't remember, but I think the two were closely related for any practical outer join query. Maybe I did use | for splits, and + for outer joins.

Andrus Adamchik - 29/Apr/08 11:55 PM
> but I think the two were closely related for any practical outer join query.

Logically splits and outer joins are not related. The importance of splits depends on a use case (in fact the use case that motivated me to expedite this feature didn't rely on splits). But I agree that splits are very important and this is a natural next step for us to do.

> Maybe I did use | for splits, and + for outer joins.

Cool. I think that's what it is.