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
