Repository: metamodel
Updated Branches:
  refs/heads/master 45edefe5a -> 76f6bb7b1


METAMODEL-133: Fixed.
Fixes #20

Project: http://git-wip-us.apache.org/repos/asf/metamodel/repo
Commit: http://git-wip-us.apache.org/repos/asf/metamodel/commit/76f6bb7b
Tree: http://git-wip-us.apache.org/repos/asf/metamodel/tree/76f6bb7b
Diff: http://git-wip-us.apache.org/repos/asf/metamodel/diff/76f6bb7b

Branch: refs/heads/master
Commit: 76f6bb7b1d9ac5a87d3138b8232bcc2943f7ef4d
Parents: 45edefe
Author: Hosur Narahari <[email protected]>
Authored: Tue May 19 22:34:55 2015 +0200
Committer: Kasper Sørensen <[email protected]>
Committed: Tue May 19 22:34:55 2015 +0200

----------------------------------------------------------------------
 CHANGES.md                                      |  1 +
 .../org/apache/metamodel/query/FromItem.java    |  5 ++
 .../metamodel/query/parser/FromItemParser.java  | 50 +++++++++++++++-----
 .../apache/metamodel/query/FromItemTest.java    | 27 +++++++++++
 4 files changed, 72 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/metamodel/blob/76f6bb7b/CHANGES.md
----------------------------------------------------------------------
diff --git a/CHANGES.md b/CHANGES.md
index 833b353..4b9acda 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -4,6 +4,7 @@
  * [METAMODEL-138] - Allow empty characteres before AS keyword.
  * [METAMODEL-141] - Improved mapping of ColumnType to SQL data types for 
Oracle, SQL Server, MySQL, DB2 and PostgreSQL
  * [METAMODEL-142] - Ensured that JDBC schema refreshes in an UpdateScript is 
using same Connection/Transaction as rest of operations
+ * [METAMODEL-133] - Improved query parser support for multiple JOINs in same 
query.
 
 ### Apache MetaModel 4.3.3
 

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76f6bb7b/core/src/main/java/org/apache/metamodel/query/FromItem.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/metamodel/query/FromItem.java 
b/core/src/main/java/org/apache/metamodel/query/FromItem.java
index 8ee07fa..392c0bf 100644
--- a/core/src/main/java/org/apache/metamodel/query/FromItem.java
+++ b/core/src/main/java/org/apache/metamodel/query/FromItem.java
@@ -267,6 +267,11 @@ public class FromItem extends BaseObject implements 
QueryItem, Cloneable {
             sb.append(superQueryAlias);
             return;
         }
+        
+        if(_join != null && _leftSide.getJoin() != null) {
+            sb.append(onItem.toSql());
+            return;
+        }
 
         if (sideAlias != null) {
             sb.append(sideAlias);

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76f6bb7b/core/src/main/java/org/apache/metamodel/query/parser/FromItemParser.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/metamodel/query/parser/FromItemParser.java 
b/core/src/main/java/org/apache/metamodel/query/parser/FromItemParser.java
index 76ff4f2..cc8916a 100644
--- a/core/src/main/java/org/apache/metamodel/query/parser/FromItemParser.java
+++ b/core/src/main/java/org/apache/metamodel/query/parser/FromItemParser.java
@@ -18,6 +18,11 @@
  */
 package org.apache.metamodel.query.parser;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
 import org.apache.metamodel.DataContext;
 import org.apache.metamodel.query.FromItem;
 import org.apache.metamodel.query.JoinType;
@@ -66,7 +71,7 @@ final class FromItemParser implements QueryPartProcessor {
                 fromItem.setAlias(alias);
             }
         } else if (itemToken.toUpperCase().indexOf(" JOIN ") != -1) {
-            fromItem = parseJoinItem(itemToken);
+            fromItem = parseAllJoinItems(itemToken);
         } else {
             fromItem = parseTableItem(itemToken);
         }
@@ -95,10 +100,32 @@ final class FromItemParser implements QueryPartProcessor {
         result.setQuery(_query);
         return result;
     }
+    
+    private FromItem parseAllJoinItems(final String itemToken) {
+        String[] joinSplit = itemToken.split("(?i) JOIN ");
+        List<String> joinsList = new ArrayList<String>();
+        for(int i = 0 ; i < joinSplit.length -1  ; i++) {
+            joinSplit[i] = joinSplit[i].trim();
+            joinSplit[i+1] = joinSplit[i+1].trim();
+            String leftPart = joinSplit[i].substring(0, 
joinSplit[i].lastIndexOf(" "));
+            String joinType = 
joinSplit[i].substring(joinSplit[i].lastIndexOf(" "));
+            String rightPart = (i+1 == joinSplit.length-1) ? joinSplit[i+1] : 
joinSplit[i+1].substring(0, joinSplit[i+1].lastIndexOf(" "));
+            joinsList.add((leftPart + " " + joinType + " JOIN " + 
rightPart).replaceAll(" +", " "));
+            String rightTable = rightPart.substring(0, 
rightPart.toUpperCase().lastIndexOf(" ON "));
+            String nextJoinType = 
joinSplit[i+1].substring(joinSplit[i+1].lastIndexOf(" "));
+            joinSplit[i+1] = rightTable + " " + nextJoinType;
+        }
+        Set<FromItem> fromItems = new HashSet<FromItem>();
+        FromItem leftFromItem = null;
+        for(String token : joinsList) {
+            leftFromItem = parseJoinItem(leftFromItem, token, fromItems);
+        }
+        return leftFromItem;
+    }
 
     // this method will be documented based on this example itemToken: FOO f
     // INNER JOIN BAR b ON f.id = b.id
-    private FromItem parseJoinItem(final String itemToken) {
+    private FromItem parseJoinItem(final FromItem leftFromItem, final String 
itemToken, Set<FromItem> fromItems) {
         final int indexOfJoin = itemToken.toUpperCase().indexOf(" JOIN ");
 
         // firstPart = "FOO f INNER"
@@ -123,6 +150,9 @@ final class FromItemParser implements QueryPartProcessor {
 
         final FromItem leftSide = parseTableItem(firstTableToken);
         final FromItem rightSide = parseTableItem(secondTableToken);
+        
+        fromItems.add(leftSide);
+        fromItems.add(rightSide);
 
         // onClausess = ["f.id = b.id"]
         final String[] onClauses = secondPart.substring(indexOfOn + " ON 
".length()).split(" AND ");
@@ -136,16 +166,17 @@ final class FromItemParser implements QueryPartProcessor {
             // rightPart = "b.id"
             final String rightPart = onClause.substring(indexOfEquals + 
1).trim();
 
-            leftOn[i] = findSelectItem(leftPart, leftSide, rightSide);
-            rightOn[i] = findSelectItem(rightPart, leftSide, rightSide);
+            leftOn[i] = findSelectItem(leftPart, fromItems.toArray(new 
FromItem[fromItems.size()]));
+            rightOn[i] = findSelectItem(rightPart, fromItems.toArray(new 
FromItem[fromItems.size()]));
         }
-
-        final FromItem result = new FromItem(joinType, leftSide, rightSide, 
leftOn, rightOn);
+        
+        final FromItem leftItem = (leftFromItem != null) ? leftFromItem : 
leftSide;
+        final FromItem result = new FromItem(joinType, leftItem, rightSide, 
leftOn, rightOn);
         result.setQuery(_query);
         return result;
     }
 
-    private SelectItem findSelectItem(String token, FromItem leftSide, 
FromItem rightSide) {
+    private SelectItem findSelectItem(String token, FromItem[] joinTables) {
         // first look in the original query
         SelectItemParser selectItemParser = new SelectItemParser(_query, 
false);
         SelectItem result = selectItemParser.findSelectItem(token);
@@ -153,18 +184,15 @@ final class FromItemParser implements QueryPartProcessor {
         if (result == null) {
             // fail over and try with the from items available in the join that
             // is being built.
-            final Query temporaryQuery = new Query().from(leftSide, rightSide);
+            final Query temporaryQuery = new Query().from(joinTables);
             selectItemParser = new SelectItemParser(temporaryQuery, false);
             result = selectItemParser.findSelectItem(token);
-
             if (result == null) {
                 throw new QueryParserException("Not capable of parsing ON 
token: " + token);
             }
 
             // set the query on the involved query parts (since they have been
             // temporarily moved to the searched query).
-            leftSide.setQuery(_query);
-            rightSide.setQuery(_query);
             result.setQuery(_query);
         }
         return result;

http://git-wip-us.apache.org/repos/asf/metamodel/blob/76f6bb7b/core/src/test/java/org/apache/metamodel/query/FromItemTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/metamodel/query/FromItemTest.java 
b/core/src/test/java/org/apache/metamodel/query/FromItemTest.java
index 4967841..b026f8d 100644
--- a/core/src/test/java/org/apache/metamodel/query/FromItemTest.java
+++ b/core/src/test/java/org/apache/metamodel/query/FromItemTest.java
@@ -18,7 +18,10 @@
  */
 package org.apache.metamodel.query;
 
+import org.apache.metamodel.MetaModelException;
 import org.apache.metamodel.MetaModelTestCase;
+import org.apache.metamodel.QueryPostprocessDataContext;
+import org.apache.metamodel.data.DataSet;
 import org.apache.metamodel.schema.Column;
 import org.apache.metamodel.schema.Relationship;
 import org.apache.metamodel.schema.Schema;
@@ -98,4 +101,28 @@ public class FromItemTest extends MetaModelTestCase {
                                "MetaModelSchema.project a LEFT JOIN (SELECT 
c.contributor_id, c.project_id AS foobar, c.name FROM MetaModelSchema.role c) b 
ON a.project_id = b.foobar",
                                from.toString());
        }
+       
+    public void testCompoundJoin() {
+        final Schema schema = getExampleSchema();
+   
+        final QueryPostprocessDataContext dc = new 
QueryPostprocessDataContext() {
+            @Override
+            protected DataSet materializeMainSchemaTable(Table table, Column[] 
columns, int maxRows) {
+                throw new UnsupportedOperationException("This method is not 
used");
+            }
+
+            @Override
+            protected String getMainSchemaName() throws MetaModelException {
+                return "MetaModelSchema";
+            }
+
+            @Override
+            protected Schema getMainSchema() throws MetaModelException {
+                return schema;
+            }
+        };
+ 
+        Query query = dc.parseQuery("SELECT c.contributor_id,p.project_id from 
contributor c INNER JOIN role r ON c.contributor_id=r.contributor_id INNER JOIN 
project p ON p.project_id=r.project_id");
+        assertEquals("SELECT c.contributor_id, p.project_id FROM 
MetaModelSchema.contributor c INNER JOIN MetaModelSchema.role r ON 
c.contributor_id = r.contributor_id INNER JOIN MetaModelSchema.project p ON 
p.project_id = r.project_id", query.toSql());
+    }
 }
\ No newline at end of file

Reply via email to