phoenix-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From maryann...@apache.org
Subject [1/3] PHOENIX-71 Support sub-joins
Date Tue, 01 Apr 2014 21:00:03 GMT
Repository: incubator-phoenix
Updated Branches:
  refs/heads/3.0 363e71bfd -> 90c69c613


http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
index a354ed1..729459d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/QueryCompiler.java
@@ -30,6 +30,7 @@ import org.apache.phoenix.compile.JoinCompiler.JoinTable;
 import org.apache.phoenix.compile.JoinCompiler.JoinedTableColumnResolver;
 import org.apache.phoenix.compile.JoinCompiler.PTableWrapper;
 import org.apache.phoenix.compile.JoinCompiler.ProjectedPTableWrapper;
+import org.apache.phoenix.compile.JoinCompiler.Table;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.execute.AggregatePlan;
 import org.apache.phoenix.execute.BasicQueryPlan;
@@ -116,37 +117,39 @@ public class QueryCompiler {
         SelectStatement select = this.select;
         List<Object> binds = statement.getParameters();
         StatementContext context = new StatementContext(statement, resolver, scan);
-        if (select.getFrom().size() > 1) {
+        if (select.isJoin()) {
             select = JoinCompiler.optimize(context, select, statement);
             if (this.select != select) {
                 ColumnResolver resolver = FromCompiler.getResolverForQuery(select, statement.getConnection());
                 context = new StatementContext(statement, resolver, scan);
             }
-            JoinSpec join = JoinCompiler.getJoinSpec(context, select);
-            return compileJoinQuery(context, select, binds, join, false);
+            JoinTable joinTable = JoinCompiler.compile(select, context.getResolver());
+            return compileJoinQuery(context, binds, joinTable, false);
         } else {
             return compileSingleQuery(context, select, binds, parallelIteratorFactory);
         }
     }
     
     @SuppressWarnings("unchecked")
-    protected QueryPlan compileJoinQuery(StatementContext context, SelectStatement select,
List<Object> binds, JoinSpec join, boolean asSubquery) throws SQLException {
+    protected QueryPlan compileJoinQuery(StatementContext context, List<Object> binds,
JoinTable joinTable, boolean asSubquery) throws SQLException {
         byte[] emptyByteArray = new byte[0];
-        List<JoinTable> joinTables = join.getJoinTables();
-        if (joinTables.isEmpty()) {
-            ProjectedPTableWrapper projectedTable = join.createProjectedTable(join.getMainTable(),
!asSubquery);
+        List<JoinSpec> joinSpecs = joinTable.getJoinSpecs();
+        if (joinSpecs.isEmpty()) {
+            Table table = joinTable.getTable();
+            ProjectedPTableWrapper projectedTable = table.createProjectedTable(!asSubquery);
             ScanProjector.serializeProjectorIntoScan(context.getScan(), JoinCompiler.getScanProjector(projectedTable));
-            context.setCurrentTable(join.getMainTable());
-            context.setResolver(join.getColumnResolver(projectedTable));
-            join.projectColumns(context.getScan(), join.getMainTable());
-            return compileSingleQuery(context, select, binds, null);
+            context.setCurrentTable(table.getTableRef());
+            context.setResolver(projectedTable.createColumnResolver());
+            table.projectColumns(context.getScan());
+            return compileSingleQuery(context, table.getAsSubquery(), binds, null);
         }
         
-        boolean[] starJoinVector = join.getStarJoinVector();
+        boolean[] starJoinVector = joinTable.getStarJoinVector();
         if (starJoinVector != null) {
-            ProjectedPTableWrapper initialProjectedTable = join.createProjectedTable(join.getMainTable(),
!asSubquery);
+            Table table = joinTable.getTable();
+            ProjectedPTableWrapper initialProjectedTable = table.createProjectedTable(!asSubquery);
             PTableWrapper projectedTable = initialProjectedTable;
-            int count = joinTables.size();
+            int count = joinSpecs.size();
             ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[count];
             List<Expression>[] joinExpressions = new List[count];
             List<Expression>[] hashExpressions = new List[count];
@@ -157,22 +160,16 @@ public class QueryCompiler {
             fieldPositions[0] = projectedTable.getTable().getColumns().size() - projectedTable.getTable().getPKColumns().size();
             boolean needsProject = asSubquery;
             for (int i = 0; i < count; i++) {
-                JoinTable joinTable = joinTables.get(i);
-                SelectStatement subStatement = joinTable.getAsSubquery();
-                if (subStatement.getFrom().size() > 1)
-                    throw new SQLFeatureNotSupportedException("Sub queries not supported.");
-                ProjectedPTableWrapper subProjTable = join.createProjectedTable(joinTable.getTable(),
false);
-                ColumnResolver resolver = join.getColumnResolver(subProjTable);
+                JoinSpec joinSpec = joinSpecs.get(i);
                 Scan subScan = ScanUtil.newScan(originalScan);
-                ScanProjector.serializeProjectorIntoScan(subScan, JoinCompiler.getScanProjector(subProjTable));
-                StatementContext subContext = new StatementContext(statement, resolver, subScan);
-                subContext.setCurrentTable(joinTable.getTable());
-                join.projectColumns(subScan, joinTable.getTable());
-                joinPlans[i] = compileSingleQuery(subContext, subStatement, binds, null);
-                boolean hasPostReference = join.hasPostReference(joinTable.getTable());
+                StatementContext subContext = new StatementContext(statement, context.getResolver(),
subScan);
+                joinPlans[i] = compileJoinQuery(subContext, binds, joinSpec.getJoinTable(),
true);
+                ColumnResolver resolver = subContext.getResolver();
+                boolean hasPostReference = joinSpec.getJoinTable().hasPostReference();
                 if (hasPostReference) {
+                    PTableWrapper subProjTable = ((JoinedTableColumnResolver) (resolver)).getPTableWrapper();
                     tables[i] = subProjTable.getTable();
-                    projectedTable = JoinCompiler.mergeProjectedTables(projectedTable, subProjTable,
joinTable.getType() == JoinType.Inner);
+                    projectedTable = projectedTable.mergeProjectedTables(subProjTable, joinSpec.getType()
== JoinType.Inner);
                     needsProject = true;
                 } else {
                     tables[i] = null;
@@ -180,12 +177,12 @@ public class QueryCompiler {
                 if (!starJoinVector[i]) {
                     needsProject = true;
                 }
-                ColumnResolver leftResolver = starJoinVector[i] ? join.getOriginalResolver()
: join.getColumnResolver(projectedTable);
+                ColumnResolver leftResolver = starJoinVector[i] ? joinTable.getOriginalResolver()
: projectedTable.createColumnResolver();
                 joinIds[i] = new ImmutableBytesPtr(emptyByteArray); // place-holder
-                Pair<List<Expression>, List<Expression>> joinConditions
= joinTable.compileJoinConditions(context, leftResolver, resolver);
+                Pair<List<Expression>, List<Expression>> joinConditions
= joinSpec.compileJoinConditions(context, leftResolver, resolver);
                 joinExpressions[i] = joinConditions.getFirst();
                 hashExpressions[i] = joinConditions.getSecond();
-                joinTypes[i] = joinTable.getType();
+                joinTypes[i] = joinSpec.getType();
                 if (i < count - 1) {
                     fieldPositions[i + 1] = fieldPositions[i] + (tables[i] == null ? 0 :
(tables[i].getColumns().size() - tables[i].getPKColumns().size()));
                 }
@@ -193,43 +190,47 @@ public class QueryCompiler {
             if (needsProject) {
                 ScanProjector.serializeProjectorIntoScan(context.getScan(), JoinCompiler.getScanProjector(initialProjectedTable));
             }
-            context.setCurrentTable(join.getMainTable());
-            context.setResolver(needsProject ? join.getColumnResolver(projectedTable) : join.getOriginalResolver());
-            join.projectColumns(context.getScan(), join.getMainTable());
-            BasicQueryPlan plan = compileSingleQuery(context, JoinCompiler.getSubqueryWithoutJoin(select,
join), binds, parallelIteratorFactory);
-            Expression postJoinFilterExpression = join.compilePostFilterExpression(context);
+            context.setCurrentTable(table.getTableRef());
+            context.setResolver(needsProject ? projectedTable.createColumnResolver() : joinTable.getOriginalResolver());
+            table.projectColumns(context.getScan());
+            BasicQueryPlan plan = compileSingleQuery(context, joinTable.getSubqueryWithoutJoin(asSubquery),
binds, parallelIteratorFactory);
+            Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
             HashJoinInfo joinInfo = new HashJoinInfo(projectedTable.getTable(), joinIds,
joinExpressions, joinTypes, starJoinVector, tables, fieldPositions, postJoinFilterExpression);
             return new HashJoinPlan(plan, joinInfo, hashExpressions, joinPlans);
         }
         
-        JoinTable lastJoinTable = joinTables.get(joinTables.size() - 1);
-        JoinType type = lastJoinTable.getType();
+        JoinSpec lastJoinSpec = joinSpecs.get(joinSpecs.size() - 1);
+        JoinType type = lastJoinSpec.getType();
         if (type == JoinType.Full)
             throw new SQLFeatureNotSupportedException("Full joins not supported.");
         
         if (type == JoinType.Right || type == JoinType.Inner) {
-            SelectStatement lhs = JoinCompiler.getSubQueryWithoutLastJoin(select, join);
-            SelectStatement rhs = JoinCompiler.getSubqueryForLastJoinTable(select, join);
-            JoinSpec lhsJoin = JoinCompiler.getSubJoinSpecWithoutPostFilters(join);
+            if (!lastJoinSpec.getJoinTable().getJoinSpecs().isEmpty())
+                throw new SQLFeatureNotSupportedException("Right join followed by sub-join
is not supported.");
+            
+            JoinTable rhsJoinTable = lastJoinSpec.getJoinTable();
+            Table rhsTable = rhsJoinTable.getTable();
+            SelectStatement rhs = rhsJoinTable.getSubqueryWithoutJoin(asSubquery);
+            JoinTable lhsJoin = joinTable.getSubJoinTableWithoutPostFilters();
             Scan subScan = ScanUtil.newScan(originalScan);
             StatementContext lhsCtx = new StatementContext(statement, context.getResolver(),
subScan);
-            QueryPlan lhsPlan = compileJoinQuery(lhsCtx, lhs, binds, lhsJoin, true);
+            QueryPlan lhsPlan = compileJoinQuery(lhsCtx, binds, lhsJoin, true);
             ColumnResolver lhsResolver = lhsCtx.getResolver();
             PTableWrapper lhsProjTable = ((JoinedTableColumnResolver) (lhsResolver)).getPTableWrapper();
-            ProjectedPTableWrapper rhsProjTable = join.createProjectedTable(lastJoinTable.getTable(),
!asSubquery);
-            ColumnResolver rhsResolver = join.getOriginalResolver();
+            ProjectedPTableWrapper rhsProjTable = rhsTable.createProjectedTable(!asSubquery);
+            ColumnResolver rhsResolver = joinTable.getOriginalResolver();
             ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[] {new ImmutableBytesPtr(emptyByteArray)};
-            Pair<List<Expression>, List<Expression>> joinConditions = lastJoinTable.compileJoinConditions(context,
lhsResolver, rhsResolver);
+            Pair<List<Expression>, List<Expression>> joinConditions = lastJoinSpec.compileJoinConditions(context,
lhsResolver, rhsResolver);
             List<Expression> joinExpressions = joinConditions.getSecond();
             List<Expression> hashExpressions = joinConditions.getFirst();
             int fieldPosition = rhsProjTable.getTable().getColumns().size() - rhsProjTable.getTable().getPKColumns().size();
-            PTableWrapper projectedTable = JoinCompiler.mergeProjectedTables(rhsProjTable,
lhsProjTable, type == JoinType.Inner);
+            PTableWrapper projectedTable = rhsProjTable.mergeProjectedTables(lhsProjTable,
type == JoinType.Inner);
             ScanProjector.serializeProjectorIntoScan(context.getScan(), JoinCompiler.getScanProjector(rhsProjTable));
-            context.setCurrentTable(lastJoinTable.getTable());
-            context.setResolver(join.getColumnResolver(projectedTable));
-            join.projectColumns(context.getScan(), lastJoinTable.getTable());
+            context.setCurrentTable(rhsTable.getTableRef());
+            context.setResolver(projectedTable.createColumnResolver());
+            rhsTable.projectColumns(context.getScan());
             BasicQueryPlan rhsPlan = compileSingleQuery(context, rhs, binds, parallelIteratorFactory);
-            Expression postJoinFilterExpression = join.compilePostFilterExpression(context);
+            Expression postJoinFilterExpression = joinTable.compilePostFilterExpression(context);
             HashJoinInfo joinInfo = new HashJoinInfo(projectedTable.getTable(), joinIds,
new List[] {joinExpressions}, new JoinType[] {type == JoinType.Inner ? type : JoinType.Left},
new boolean[] {true}, new PTable[] {lhsProjTable.getTable()}, new int[] {fieldPosition}, postJoinFilterExpression);
             return new HashJoinPlan(rhsPlan, joinInfo, new List[] {hashExpressions}, new
QueryPlan[] {lhsPlan});
         }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
index 765ebb8..0435301 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/StatementNormalizer.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.compile;
 
 import java.sql.SQLException;
 import java.sql.SQLFeatureNotSupportedException;
+import java.util.Collections;
 import java.util.List;
 
 import com.google.common.collect.Lists;
@@ -74,8 +75,7 @@ public class StatementNormalizer extends ParseNodeRewriter {
      * @throws SQLException 
      */
     public static SelectStatement normalize(SelectStatement statement, ColumnResolver resolver)
throws SQLException {
-        List<TableNode> from = statement.getFrom();
-        boolean multiTable = from.size() > 1;
+        boolean multiTable = statement.isJoin();
         // Replace WildcardParse with a list of TableWildcardParseNode for multi-table queries
         if (multiTable) {
             List<AliasedNode> selectNodes = statement.getSelect();
@@ -87,11 +87,13 @@ public class StatementNormalizer extends ParseNodeRewriter {
                     if (selectNodes == normSelectNodes) {
                         normSelectNodes = Lists.newArrayList(selectNodes.subList(0, i));
                     }
-                    for (TableNode tNode : from) {
+                    for (TableNode tNode : statement.getFrom()) {
                         TableNameVisitor visitor = new TableNameVisitor();
-                        tNode.accept(visitor);
-                        TableWildcardParseNode node = NODE_FACTORY.tableWildcard(visitor.getTableName());
-                        normSelectNodes.add(NODE_FACTORY.aliasedNode(null, node));
+                        List<TableName> tableNames = tNode.accept(visitor);
+                        for (TableName tableName : tableNames) {
+                            TableWildcardParseNode node = NODE_FACTORY.tableWildcard(tableName);
+                            normSelectNodes.add(NODE_FACTORY.aliasedNode(null, node));
+                        }
                     }
                 } else if (selectNodes != normSelectNodes) {
                     normSelectNodes.add(aliasedNode);
@@ -107,31 +109,33 @@ public class StatementNormalizer extends ParseNodeRewriter {
         return rewrite(statement, new StatementNormalizer(resolver, statement.getSelect().size(),
multiTable));
     }
 
-    private static class TableNameVisitor implements TableNodeVisitor {
-        private TableName tableName;
-        
-        public TableName getTableName() {
-            return tableName;
-        }
+    private static class TableNameVisitor implements TableNodeVisitor<List<TableName>>
{
 
         @Override
-        public void visit(BindTableNode boundTableNode) throws SQLException {
-            tableName = boundTableNode.getAlias() == null ? boundTableNode.getName() : TableName.create(null,
boundTableNode.getAlias());
+        public List<TableName> visit(BindTableNode boundTableNode) throws SQLException
{
+            TableName name = boundTableNode.getAlias() == null ? boundTableNode.getName()
: TableName.create(null, boundTableNode.getAlias());
+            return Collections.singletonList(name);
         }
 
         @Override
-        public void visit(JoinTableNode joinNode) throws SQLException {
-            joinNode.getTable().accept(this);
+        public List<TableName> visit(JoinTableNode joinNode) throws SQLException {
+            List<TableName> lhs = joinNode.getLHS().accept(this);
+            List<TableName> rhs = joinNode.getRHS().accept(this);
+            List<TableName> ret = Lists.<TableName>newArrayListWithExpectedSize(lhs.size()
+ rhs.size());
+            ret.addAll(lhs);
+            ret.addAll(rhs);
+            return ret;
         }
 
         @Override
-        public void visit(NamedTableNode namedTableNode)
+        public List<TableName> visit(NamedTableNode namedTableNode)
                 throws SQLException {
-            tableName = namedTableNode.getAlias() == null ? namedTableNode.getName() : TableName.create(null,
namedTableNode.getAlias());
+            TableName name = namedTableNode.getAlias() == null ? namedTableNode.getName()
: TableName.create(null, namedTableNode.getAlias());
+            return Collections.singletonList(name);
         }
 
         @Override
-        public void visit(DerivedTableNode subselectNode)
+        public List<TableName> visit(DerivedTableNode subselectNode)
                 throws SQLException {
             throw new SQLFeatureNotSupportedException();
         }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
index d1df867..a32ebaa 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/optimize/QueryOptimizer.java
@@ -80,7 +80,7 @@ public class QueryOptimizer {
         // TODO: the recompile for the index tables could skip the normalize step
         SelectStatement select = (SelectStatement)dataPlan.getStatement();
         // TODO: consider not even compiling index plans if we have a point lookup
-        if (!useIndexes || select.getFrom().size() > 1) {
+        if (!useIndexes || select.isJoin()) {
             return dataPlan;
         }
         PTable dataTable = dataPlan.getTableRef().getTable();

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/BindTableNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/BindTableNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/BindTableNode.java
index c83b585..52a8948 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/BindTableNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/BindTableNode.java
@@ -35,8 +35,8 @@ public class BindTableNode extends ConcreteTableNode {
     }
 
     @Override
-    public void accept(TableNodeVisitor visitor) throws SQLException {
-        visitor.visit(this);
+    public <T> T accept(TableNodeVisitor<T> visitor) throws SQLException {
+        return visitor.visit(this);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/DerivedTableNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/DerivedTableNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/DerivedTableNode.java
index 0fcde20..b86c76d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/DerivedTableNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/DerivedTableNode.java
@@ -44,8 +44,8 @@ public class DerivedTableNode extends TableNode {
     }
 
     @Override
-    public void accept(TableNodeVisitor visitor) throws SQLException {
-        visitor.visit(this);
+    public <T> T accept(TableNodeVisitor<T> visitor) throws SQLException {
+        return visitor.visit(this);
     }
 
 }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinPartNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinPartNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinPartNode.java
new file mode 100644
index 0000000..cdbaaea
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinPartNode.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.parse;
+
+import org.apache.phoenix.parse.JoinTableNode.JoinType;
+
+/**
+ * 
+ * Node representing the partial join clause in the FROM clause of SQL
+ *
+ * 
+ * @since 0.1
+ */
+class JoinPartNode {
+    
+    private final JoinType type;
+    private final ParseNode onNode;
+    private final TableNode table;
+    
+    JoinPartNode(JoinType type, ParseNode onNode, TableNode table) {
+        this.type = type;
+        this.onNode = onNode;
+        this.table = table;
+    }
+
+    JoinType getType() {
+        return type;
+    }
+    
+    ParseNode getOnNode() {
+        return onNode;
+    }
+    
+    TableNode getTable() {
+        return table;
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinTableNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinTableNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinTableNode.java
index 1e06b5e..cbd6bce 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinTableNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/JoinTableNode.java
@@ -32,32 +32,37 @@ public class JoinTableNode extends TableNode {
     public enum JoinType {Inner, Left, Right, Full};
     
     private final JoinType type;
-    private final ParseNode on;
-    private final TableNode table;
+    private final TableNode lhs;
+    private final TableNode rhs;
+    private final ParseNode onNode;
     
-    JoinTableNode(JoinType type, ParseNode on, TableNode table) {
-        super(table.getAlias());
+    JoinTableNode(JoinType type, TableNode lhs, TableNode rhs, ParseNode onNode) {
+        super(null);
         this.type = type;
-        this.on = on;
-        this.table = table;
+        this.lhs = lhs;
+        this.rhs = rhs;
+        this.onNode = onNode;
     }
-
+    
     public JoinType getType() {
         return type;
     }
-
-    public ParseNode getOnNode() {
-        return on;
+    
+    public TableNode getLHS() {
+        return lhs;
     }
     
-    public TableNode getTable() {
-        return table;
+    public TableNode getRHS() {
+        return rhs;
+    }
+    
+    public ParseNode getOnNode() {
+        return onNode;
     }
 
     @Override
-    public void accept(TableNodeVisitor visitor) throws SQLException {
-        visitor.visit(this);
+    public <T> T accept(TableNodeVisitor<T> visitor) throws SQLException {
+        return visitor.visit(this);
     }
-
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedTableNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedTableNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedTableNode.java
index 065c7b2..9379919 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedTableNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/NamedTableNode.java
@@ -52,8 +52,8 @@ public class NamedTableNode extends ConcreteTableNode {
     }
 
     @Override
-    public void accept(TableNodeVisitor visitor) throws SQLException {
-        visitor.visit(this);
+    public <T> T accept(TableNodeVisitor<T> visitor) throws SQLException {
+        return visitor.visit(this);
     }
 
     public List<ColumnDef> getDynamicColumns() {

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
index 2523e29..41f4c6c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeFactory.java
@@ -397,8 +397,20 @@ public class ParseNodeFactory {
         return new IsNullParseNode(child, negate);
     }
 
-    public JoinTableNode join (JoinType type, ParseNode on, TableNode table) {
-        return new JoinTableNode(type, on, table);
+    public TableNode table(TableNode table, List<JoinPartNode> parts) {
+        for (JoinPartNode part : parts) {
+            table = new JoinTableNode(part.getType(), table, part.getTable(), part.getOnNode());
+        }
+        
+        return table;
+    }
+    
+    JoinPartNode joinPart(JoinType type, ParseNode onNode, TableNode table) {
+        return new JoinPartNode(type, onNode, table);
+    }
+
+    public JoinTableNode join(JoinType type, TableNode lhs, TableNode rhs, ParseNode on)
{
+        return new JoinTableNode(type, lhs, rhs, on);
     }
 
     public DerivedTableNode derivedTable (String alias, SelectStatement select) {

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
index 6e15c0f..bff5834 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/ParseNodeRewriter.java
@@ -59,29 +59,21 @@ public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode>
{
         Map<String,ParseNode> aliasMap = rewriter.getAliasMap();
         List<TableNode> from = statement.getFrom();
         List<TableNode> normFrom = from;
-        if (from.size() > 1) {
-        	for (int i = 1; i < from.size(); i++) {
-        		TableNode tableNode = from.get(i);
-        		if (tableNode instanceof JoinTableNode) {
-        			JoinTableNode joinTableNode = (JoinTableNode) tableNode;
-        			ParseNode onNode = joinTableNode.getOnNode();
-        			rewriter.reset();
-        			ParseNode normOnNode = onNode.accept(rewriter);
-        			if (onNode == normOnNode) {
-        				if (from != normFrom) {
-        					normFrom.add(tableNode);
-        				}
-        				continue;
-        			}
-        			if (from == normFrom) {
-        				normFrom = Lists.newArrayList(from.subList(0, i));
-        			}
-        			TableNode normTableNode = NODE_FACTORY.join(joinTableNode.getType(), normOnNode,
joinTableNode.getTable());
-        			normFrom.add(normTableNode);
-        		} else if (from != normFrom) {
-					normFrom.add(tableNode);
-				}
-        	}
+        TableNodeRewriter tableNodeRewriter = new TableNodeRewriter(rewriter);
+        for (int i = 0; i < from.size(); i++) {
+            TableNode tableNode = from.get(i);
+            tableNodeRewriter.reset();
+            TableNode normTableNode = tableNode.accept(tableNodeRewriter);
+            if (normTableNode == tableNode) {
+                if (from != normFrom) {
+                    normFrom.add(tableNode);
+                }
+                continue;
+            }
+            if (from == normFrom) {
+                normFrom = Lists.newArrayList(from.subList(0, i));        		    
+            }
+            normFrom.add(normTableNode);
         }
         ParseNode where = statement.getWhere();
         ParseNode normWhere = where;
@@ -497,4 +489,47 @@ public class ParseNodeRewriter extends TraverseAllParseNodeVisitor<ParseNode>
{
             }
         });
 	}
+	
+	private static class TableNodeRewriter implements TableNodeVisitor<TableNode> {
+	    private final ParseNodeRewriter parseNodeRewriter;
+	    
+	    public TableNodeRewriter(ParseNodeRewriter parseNodeRewriter) {
+	        this.parseNodeRewriter = parseNodeRewriter;
+	    }
+	    
+	    public void reset() {
+	    }
+
+        @Override
+        public TableNode visit(BindTableNode boundTableNode) throws SQLException {
+            return boundTableNode;
+        }
+
+        @Override
+        public TableNode visit(JoinTableNode joinNode) throws SQLException {
+            TableNode lhsNode = joinNode.getLHS();
+            TableNode rhsNode = joinNode.getRHS();
+            ParseNode onNode = joinNode.getOnNode();
+            TableNode normLhsNode = lhsNode.accept(this);
+            TableNode normRhsNode = rhsNode.accept(this);
+            parseNodeRewriter.reset();
+            ParseNode normOnNode = onNode.accept(parseNodeRewriter);
+            if (lhsNode == normLhsNode && rhsNode == normRhsNode && onNode
== normOnNode)
+                return joinNode;
+            
+            return NODE_FACTORY.join(joinNode.getType(), normLhsNode, normRhsNode, normOnNode);
+        }
+
+        @Override
+        public TableNode visit(NamedTableNode namedTableNode) throws SQLException {
+            return namedTableNode;
+            
+        }
+
+        @Override
+        public TableNode visit(DerivedTableNode subselectNode) throws SQLException {
+            return subselectNode;
+        }
+	    
+	}
 }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
index c3c0f8d..8caea30 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/SelectStatement.java
@@ -186,4 +186,8 @@ public class SelectStatement implements FilterableStatement {
     public Action getSequenceAction() {
         return Action.RESERVE;
     }
+    
+    public boolean isJoin() {
+        return fromTable.size() > 1 || (fromTable.size() > 0 && fromTable.get(0)
instanceof JoinTableNode);
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNode.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNode.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNode.java
index 66cff35..7ab8d0c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNode.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNode.java
@@ -39,6 +39,6 @@ public abstract class TableNode {
         return alias;
     }
 
-    public abstract void accept(TableNodeVisitor visitor) throws SQLException;
+    public abstract <T> T accept(TableNodeVisitor<T> visitor) throws SQLException;
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNodeVisitor.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNodeVisitor.java b/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNodeVisitor.java
index f275275..8d5e4e7 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNodeVisitor.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/parse/TableNodeVisitor.java
@@ -27,9 +27,9 @@ import java.sql.SQLException;
  * 
  * @since 0.1
  */
-public interface TableNodeVisitor {
-    void visit(BindTableNode boundTableNode) throws SQLException;
-    void visit(JoinTableNode joinNode) throws SQLException;
-    void visit(NamedTableNode namedTableNode) throws SQLException;
-    void visit(DerivedTableNode subselectNode) throws SQLException;
+public interface TableNodeVisitor<E> {
+    E visit(BindTableNode boundTableNode) throws SQLException;
+    E visit(JoinTableNode joinNode) throws SQLException;
+    E visit(NamedTableNode namedTableNode) throws SQLException;
+    E visit(DerivedTableNode subselectNode) throws SQLException;
 }

http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/90c69c61/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
index e6d5f69..b64fe56 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/compile/JoinQueryCompilerTest.java
@@ -34,7 +34,7 @@ import java.sql.ResultSet;
 import java.sql.SQLException;
 
 import org.apache.hadoop.hbase.client.Scan;
-import org.apache.phoenix.compile.JoinCompiler.JoinSpec;
+import org.apache.phoenix.compile.JoinCompiler.JoinTable;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixStatement;
 import org.apache.phoenix.parse.SQLParser;
@@ -80,62 +80,62 @@ public class JoinQueryCompilerTest extends BaseConnectionlessQueryTest
{
                 + "WHERE t1.\"item_id\" = '0000000001' AND t2.\"item_id\" = '0000000002'
AND t3.\"item_id\" = '0000000003'";
 
         String query = String.format(queryTemplate, "INNER", "INNER");
-        JoinSpec joinSpec = getJoinSpec(query, pconn);
-        assertEquals(1, joinSpec.getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        JoinTable joinTable = getJoinTable(query, pconn);
+        assertEquals(1, joinTable.getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "INNER", "LEFT");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(1, joinSpec.getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(1, joinTable.getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "INNER", "RIGHT");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(0, joinSpec.getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(0, joinTable.getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "LEFT", "INNER");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(1, joinSpec.getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(1, joinTable.getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "LEFT", "LEFT");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(1, joinSpec.getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(1, joinTable.getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "LEFT", "RIGHT");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(0, joinSpec.getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(0, joinTable.getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "RIGHT", "INNER");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(0, joinSpec.getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(0, joinTable.getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
 
         query = String.format(queryTemplate, "RIGHT", "RIGHT");
-        joinSpec = getJoinSpec(query, pconn);
-        assertEquals(0, joinSpec.getPreFilters().size());
-        assertEquals(0, joinSpec.getJoinTables().get(0).getPreFilters().size());
-        assertEquals(1, joinSpec.getJoinTables().get(1).getPreFilters().size());
+        joinTable = getJoinTable(query, pconn);
+        assertEquals(0, joinTable.getTable().getPreFilters().size());
+        assertEquals(0, joinTable.getJoinSpecs().get(0).getJoinTable().getTable().getPreFilters().size());
+        assertEquals(1, joinTable.getJoinSpecs().get(1).getJoinTable().getTable().getPreFilters().size());
     }
     
-    private static JoinSpec getJoinSpec(String query, PhoenixConnection connection) throws
SQLException {
+    private static JoinTable getJoinTable(String query, PhoenixConnection connection) throws
SQLException {
         Scan scan = new Scan();
         SQLParser parser = new SQLParser(query);
         SelectStatement select = parser.parseQuery();
         ColumnResolver resolver = FromCompiler.getResolverForQuery(select, connection);
         select = StatementNormalizer.normalize(select, resolver);
         StatementContext context = new StatementContext(new PhoenixStatement(connection),
resolver, scan);
-        return JoinCompiler.getJoinSpec(context, select);        
+        return JoinCompiler.compile(select, context.getResolver());        
     }
 }
 


Mime
View raw message