You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by ma...@apache.org on 2014/04/01 02:34:48 UTC
[1/3] PHOENIX-71 Support sub-joins
Repository: incubator-phoenix
Updated Branches:
refs/heads/master 0849b259d -> 12bd7aedd
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/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/12bd7aed/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/12bd7aed/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 220ef89..b275e3d 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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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/12bd7aed/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());
}
}
[3/3] git commit: PHOENIX-71 Support sub-joins
Posted by ma...@apache.org.
PHOENIX-71 Support sub-joins
Project: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/commit/12bd7aed
Tree: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/tree/12bd7aed
Diff: http://git-wip-us.apache.org/repos/asf/incubator-phoenix/diff/12bd7aed
Branch: refs/heads/master
Commit: 12bd7aeddd4e25a754b76c3f05b00730c4304644
Parents: 0849b25
Author: maryannxue <ma...@apache.org>
Authored: Mon Mar 31 20:33:50 2014 -0400
Committer: maryannxue <ma...@apache.org>
Committed: Mon Mar 31 20:33:50 2014 -0400
----------------------------------------------------------------------
.../org/apache/phoenix/end2end/HashJoinIT.java | 258 +++-
phoenix-core/src/main/antlr3/PhoenixSQL.g | 23 +-
.../apache/phoenix/compile/FromCompiler.java | 17 +-
.../apache/phoenix/compile/JoinCompiler.java | 1425 +++++++++---------
.../apache/phoenix/compile/QueryCompiler.java | 99 +-
.../phoenix/compile/StatementNormalizer.java | 42 +-
.../apache/phoenix/optimize/QueryOptimizer.java | 2 +-
.../org/apache/phoenix/parse/BindTableNode.java | 4 +-
.../apache/phoenix/parse/DerivedTableNode.java | 4 +-
.../org/apache/phoenix/parse/JoinPartNode.java | 53 +
.../org/apache/phoenix/parse/JoinTableNode.java | 35 +-
.../apache/phoenix/parse/NamedTableNode.java | 4 +-
.../apache/phoenix/parse/ParseNodeFactory.java | 16 +-
.../apache/phoenix/parse/ParseNodeRewriter.java | 81 +-
.../apache/phoenix/parse/SelectStatement.java | 4 +
.../org/apache/phoenix/parse/TableNode.java | 2 +-
.../apache/phoenix/parse/TableNodeVisitor.java | 10 +-
.../phoenix/compile/JoinQueryCompilerTest.java | 70 +-
18 files changed, 1192 insertions(+), 957 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
index 248621b..5ccef2c 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/HashJoinIT.java
@@ -226,7 +226,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" BUILD HASH TABLE 1\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
/*
- * testSelfJoin
+ * testSelfJoin()
* SELECT i2.item_id, i1.name FROM joinItemTable i1
* JOIN joinItemTable i2 ON i1.item_id = i2.item_id
* ORDER BY i1.item_id
@@ -237,7 +237,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
" SERVER FILTER BY FIRST KEY ONLY",
/*
- * testSelfJoin
+ * testSelfJoin()
* SELECT i1.name, i2.name FROM joinItemTable i1
* JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id
* ORDER BY i1.name, i2.name
@@ -249,12 +249,12 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" BUILD HASH TABLE 0\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME,
/*
- * testStarJoin
- * SELECT order_id, c.name, i.name iname, quantity, o.date
- * FROM joinOrderTable o
- * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
- * JOIN joinItemTable i ON o.item_id = i.item_id
- * ORDER BY order_id
+ * testStarJoin()
+ * SELECT order_id, c.name, i.name iname, quantity, o.date
+ * FROM joinOrderTable o
+ * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
+ * JOIN joinItemTable i ON o.item_id = i.item_id
+ * ORDER BY order_id
*/
"CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
" PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
@@ -263,12 +263,12 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" BUILD HASH TABLE 1\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME,
/*
- * testStarJoin
- * SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date
- * FROM joinOrderTable o
- * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
- * JOIN joinItemTable i ON o.item_id = i.item_id
- * ORDER BY order_id
+ * testStarJoin()
+ * SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date
+ * FROM joinOrderTable o
+ * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
+ * JOIN joinItemTable i ON o.item_id = i.item_id
+ * ORDER BY order_id
*/
"CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
" SERVER SORTED BY [O.order_id]\n" +
@@ -279,6 +279,33 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
" BUILD HASH TABLE 0\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME,
+ /*
+ * testSubJoin()
+ * SELECT * FROM joinCustomerTable c
+ * INNER JOIN (joinOrderTable o
+ * INNER JOIN (joinSupplierTable s
+ * RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+ * ON o.item_id = i.item_id)
+ * ON c.customer_id = o.customer_id
+ * WHERE c.customer_id <= '0000000005'
+ * AND order_id != '000000000000003'
+ * AND i.name != 'T3'
+ * ORDER BY c.customer_id, i.name
+ */
+ "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
+ " SERVER SORTED BY [C.customer_id, I.NAME]\n" +
+ "CLIENT MERGE SORT\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+ " SERVER FILTER BY order_id != '000000000000003'\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ITEM_TABLE_DISPLAY_NAME + "\n" +
+ " SERVER FILTER BY NAME != 'T3'\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SUPPLIER_TABLE_DISPLAY_NAME,
}});
testCases.add(new String[][] {
{
@@ -404,7 +431,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" BUILD HASH TABLE 1\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier",
/*
- * testSelfJoin
+ * testSelfJoin()
* SELECT i2.item_id, i1.name FROM joinItemTable i1
* JOIN joinItemTable i2 ON i1.item_id = i2.item_id
* ORDER BY i1.item_id
@@ -415,7 +442,7 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
" SERVER FILTER BY FIRST KEY ONLY",
/*
- * testSelfJoin
+ * testSelfJoin()
* SELECT i1.name, i2.name FROM joinItemTable i1
* JOIN joinItemTable i2 ON i1.item_id = i2.supplier_id
* ORDER BY i1.name, i2.name
@@ -428,12 +455,12 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" BUILD HASH TABLE 0\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item",
/*
- * testStarJoin
- * SELECT order_id, c.name, i.name iname, quantity, o.date
- * FROM joinOrderTable o
- * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
- * JOIN joinItemTable i ON o.item_id = i.item_id
- * ORDER BY order_id
+ * testStarJoin()
+ * SELECT order_id, c.name, i.name iname, quantity, o.date
+ * FROM joinOrderTable o
+ * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
+ * JOIN joinItemTable i ON o.item_id = i.item_id
+ * ORDER BY order_id
*/
"CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
" PARALLEL EQUI-JOIN 2 HASH TABLES:\n" +
@@ -443,12 +470,12 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
" SERVER FILTER BY FIRST KEY ONLY",
/*
- * testStarJoin
- * SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date
- * FROM joinOrderTable o
- * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
- * JOIN joinItemTable i ON o.item_id = i.item_id
- * ORDER BY order_id
+ * testStarJoin()
+ * SELECT (*NO_STAR_JOIN*) order_id, c.name, i.name iname, quantity, o.date
+ * FROM joinOrderTable o
+ * JOIN joinCustomerTable c ON o.customer_id = c.customer_id
+ * JOIN joinItemTable i ON o.item_id = i.item_id
+ * ORDER BY order_id
*/
"CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
" SERVER FILTER BY FIRST KEY ONLY\n" +
@@ -460,6 +487,33 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
" PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
" BUILD HASH TABLE 0\n" +
" CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_customer",
+ /*
+ * testSubJoin()
+ * SELECT * FROM joinCustomerTable c
+ * INNER JOIN (joinOrderTable o
+ * INNER JOIN (joinSupplierTable s
+ * RIGHT JOIN joinItemTable i ON i.supplier_id = s.supplier_id)
+ * ON o.item_id = i.item_id)
+ * ON c.customer_id = o.customer_id
+ * WHERE c.customer_id <= '0000000005'
+ * AND order_id != '000000000000003'
+ * AND i.name != 'T3'
+ * ORDER BY c.customer_id, i.name
+ */
+ "CLIENT PARALLEL 1-WAY RANGE SCAN OVER " + JOIN_CUSTOMER_TABLE_DISPLAY_NAME + " [*] - ['0000000005']\n" +
+ " SERVER SORTED BY [C.customer_id, I.0:NAME]\n" +
+ "CLIENT MERGE SORT\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_ORDER_TABLE_DISPLAY_NAME + "\n" +
+ " SERVER FILTER BY order_id != '000000000000003'\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_item\n" +
+ " SERVER FILTER BY NAME != 'T3'\n" +
+ " PARALLEL EQUI-JOIN 1 HASH TABLES:\n" +
+ " BUILD HASH TABLE 0\n" +
+ " CLIENT PARALLEL 1-WAY FULL SCAN OVER " + JOIN_SCHEMA + ".idx_supplier",
}});
return testCases;
}
@@ -1309,13 +1363,16 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
@Test
public void testLeftRightJoin() throws Exception {
- String query = "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
- + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
- + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+ String query1 = "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
+ + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
+ + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
+ String query2 = "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
+ + "(" + JOIN_ITEM_TABLE_FULL_NAME + " i RIGHT JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\")"
+ + " ON o.\"item_id\" = i.\"item_id\" ORDER BY \"order_id\", s.\"supplier_id\" DESC";
Properties props = new Properties(TEST_PROPERTIES);
Connection conn = DriverManager.getConnection(getUrl(), props);
try {
- PreparedStatement statement = conn.prepareStatement(query);
+ PreparedStatement statement = conn.prepareStatement(query1);
ResultSet rs = statement.executeQuery();
assertTrue (rs.next());
assertNull(rs.getString(1));
@@ -1367,21 +1424,9 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
assertNotNull(rs.getDate(5));
assertFalse(rs.next());
- } finally {
- conn.close();
- }
- }
-
- @Test
- public void testMultiLeftJoin() throws Exception {
- String query = "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
- + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
- + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\"";
- Properties props = new Properties(TEST_PROPERTIES);
- Connection conn = DriverManager.getConnection(getUrl(), props);
- try {
- PreparedStatement statement = conn.prepareStatement(query);
- ResultSet rs = statement.executeQuery();
+
+ statement = conn.prepareStatement(query2);
+ rs = statement.executeQuery();
assertTrue (rs.next());
assertEquals(rs.getString(1), "000000000000001");
assertEquals(rs.getString(2), "T1");
@@ -1420,6 +1465,59 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
}
@Test
+ public void testMultiLeftJoin() throws Exception {
+ String[] queries = {
+ "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
+ + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+ + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\"",
+ "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
+ + "(" + JOIN_ITEM_TABLE_FULL_NAME + " i LEFT JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " s ON i.\"supplier_id\" = s.\"supplier_id\") "
+ + "ON o.\"item_id\" = i.\"item_id\""};
+ Properties props = new Properties(TEST_PROPERTIES);
+ Connection conn = DriverManager.getConnection(getUrl(), props);
+ try {
+ for (String query : queries) {
+ PreparedStatement statement = conn.prepareStatement(query);
+ ResultSet rs = statement.executeQuery();
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "000000000000001");
+ assertEquals(rs.getString(2), "T1");
+ assertEquals(rs.getString(3), "S1");
+ assertEquals(rs.getInt(4), 1000);
+ assertNotNull(rs.getDate(5));
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "000000000000002");
+ assertEquals(rs.getString(2), "T6");
+ assertEquals(rs.getString(3), "S6");
+ assertEquals(rs.getInt(4), 2000);
+ assertNotNull(rs.getDate(5));
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "000000000000003");
+ assertEquals(rs.getString(2), "T2");
+ assertEquals(rs.getString(3), "S1");
+ assertEquals(rs.getInt(4), 3000);
+ assertNotNull(rs.getDate(5));
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "000000000000004");
+ assertEquals(rs.getString(2), "T6");
+ assertEquals(rs.getString(3), "S6");
+ assertEquals(rs.getInt(4), 4000);
+ assertNotNull(rs.getDate(5));
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "000000000000005");
+ assertEquals(rs.getString(2), "T3");
+ assertEquals(rs.getString(3), "S2");
+ assertEquals(rs.getInt(4), 5000);
+ assertNotNull(rs.getDate(5));
+
+ assertFalse(rs.next());
+ }
+ } finally {
+ conn.close();
+ }
+ }
+
+ @Test
public void testMultiRightJoin() throws Exception {
String query = "SELECT \"order_id\", i.name, s.name, quantity, date FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o RIGHT JOIN "
+ JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" RIGHT JOIN "
@@ -2306,6 +2404,72 @@ public class HashJoinIT extends BaseHBaseManagedTimeIT {
}
}
+
+ @Test
+ public void testSubJoin() throws Exception {
+ String query1 = "SELECT i.name, count(c.name), min(s.name), max(quantity) FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
+ + "(" + JOIN_SUPPLIER_TABLE_FULL_NAME + " s RIGHT JOIN " + JOIN_ITEM_TABLE_FULL_NAME + " i ON i.\"supplier_id\" = s.\"supplier_id\")"
+ + " ON o.\"item_id\" = i.\"item_id\" LEFT JOIN "
+ + JOIN_CUSTOMER_TABLE_FULL_NAME + " c ON c.\"customer_id\" = o.\"customer_id\" GROUP BY i.name ORDER BY i.name";
+ String query2 = "SELECT c.name, o.\"order_id\", i.name, s.name FROM " + JOIN_CUSTOMER_TABLE_FULL_NAME + " c INNER JOIN "
+ + "(" + JOIN_ORDER_TABLE_FULL_NAME + " o INNER JOIN "
+ + "(" + JOIN_SUPPLIER_TABLE_FULL_NAME + " s RIGHT JOIN " + JOIN_ITEM_TABLE_FULL_NAME + " i ON i.\"supplier_id\" = s.\"supplier_id\")"
+ + " ON o.\"item_id\" = i.\"item_id\") ON c.\"customer_id\" = o.\"customer_id\""
+ + " WHERE c.\"customer_id\" <= '0000000005' AND \"order_id\" != '000000000000003' AND i.name != 'T3' ORDER BY c.\"customer_id\", i.name";
+ Properties props = new Properties(TEST_PROPERTIES);
+ Connection conn = DriverManager.getConnection(getUrl(), props);
+ try {
+ PreparedStatement statement = conn.prepareStatement(query1);
+ ResultSet rs = statement.executeQuery();
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "T1");
+ assertEquals(rs.getInt(2), 1);
+ assertEquals(rs.getString(3), "S1");
+ assertEquals(rs.getInt(4), 1000);
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "T2");
+ assertEquals(rs.getInt(2), 1);
+ assertEquals(rs.getString(3), "S1");
+ assertEquals(rs.getInt(4), 3000);
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "T3");
+ assertEquals(rs.getInt(2), 1);
+ assertEquals(rs.getString(3), "S2");
+ assertEquals(rs.getInt(4), 5000);
+ assertTrue (rs.next());
+ assertEquals(rs.getString(1), "T6");
+ assertEquals(rs.getInt(2), 2);
+ assertEquals(rs.getString(3), "S6");
+ assertEquals(rs.getInt(4), 4000);
+
+ assertFalse(rs.next());
+
+ statement = conn.prepareStatement(query2);
+ rs = statement.executeQuery();
+ assertTrue(rs.next());
+ assertEquals(rs.getString("c.name"), "C3");
+ assertEquals(rs.getString("O.order_id"), "000000000000002");
+ assertEquals(rs.getString("i.name"), "T6");
+ assertEquals(rs.getString("s.name"), "S6");
+ assertTrue(rs.next());
+ assertEquals(rs.getString("c.name"), "C4");
+ assertEquals(rs.getString("O.order_id"), "000000000000001");
+ assertEquals(rs.getString("i.name"), "T1");
+ assertEquals(rs.getString("s.name"), "S1");
+ assertTrue(rs.next());
+ assertEquals(rs.getString("c.name"), "C4");
+ assertEquals(rs.getString("O.order_id"), "000000000000004");
+ assertEquals(rs.getString("i.name"), "T6");
+ assertEquals(rs.getString("s.name"), "S6");
+
+ assertFalse(rs.next());
+
+ rs = conn.createStatement().executeQuery("EXPLAIN " + query2);
+ assertEquals(plans[13], QueryUtil.getExplainPlan(rs));
+ } finally {
+ conn.close();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/phoenix-core/src/main/antlr3/PhoenixSQL.g
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/antlr3/PhoenixSQL.g b/phoenix-core/src/main/antlr3/PhoenixSQL.g
index 6ccd03e..4f1d107 100644
--- a/phoenix-core/src/main/antlr3/PhoenixSQL.g
+++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g
@@ -629,23 +629,28 @@ parseOrderByField returns [OrderByNode ret]
parseFrom returns [List<TableNode> ret]
@init{ret = new ArrayList<TableNode>(4); }
- : t=table_ref {$ret.add(t);} (s=sub_table_ref { $ret.add(s); })*
- ;
-
-sub_table_ref returns [TableNode ret]
- : COMMA t=table_ref { $ret = t; }
- | t=join_spec { $ret = t; }
+ : t=table_ref {$ret.add(t);} (COMMA s=table_ref { $ret.add(s); })*
;
table_ref returns [TableNode ret]
+ : t=single_table_ref p=join_parts { $ret = factory.table(t, p); }
+ ;
+
+single_table_ref returns [TableNode ret]
: n=bind_name ((AS)? alias=identifier)? { $ret = factory.bindTable(alias, factory.table(null,n)); } // TODO: review
| t=from_table_name ((AS)? alias=identifier)? (LPAREN cdefs=dyn_column_defs RPAREN)? { $ret = factory.namedTable(alias,t,cdefs); }
| LPAREN SELECT s=hinted_select_node RPAREN ((AS)? alias=identifier)? { $ret = factory.derivedTable(alias, s); }
;
-join_spec returns [TableNode ret]
- : j=join_type JOIN t=table_ref ON e=expression { $ret = factory.join(j, e, t); }
- ;
+join_parts returns [List<JoinPartNode> ret]
+@init{ret = new ArrayList<JoinPartNode>(4); }
+ : (p=join_part { $ret.add(p); })*
+ ;
+
+join_part returns [JoinPartNode ret]
+ : j=join_type JOIN r=single_table_ref ON e=expression { $ret = factory.joinPart(j, e, r); }
+ | j=join_type JOIN LPAREN r=table_ref RPAREN ON e=expression { $ret = factory.joinPart(j, e, r); }
+ ;
join_type returns [JoinTableNode.JoinType ret]
: INNER? { $ret = JoinTableNode.JoinType.Inner; }
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
index f43ff4f..500ae5f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/FromCompiler.java
@@ -146,7 +146,7 @@ public class FromCompiler {
public static ColumnResolver getResolverForQuery(SelectStatement statement, PhoenixConnection connection)
throws SQLException {
List<TableNode> fromNodes = statement.getFrom();
- if (fromNodes.size() == 1)
+ if (!statement.isJoin())
return new SingleTableColumnResolver(connection, (NamedTableNode)fromNodes.get(0), true);
MultiTableColumnResolver visitor = new MultiTableColumnResolver(connection);
@@ -337,7 +337,7 @@ public class FromCompiler {
}
// TODO: unused, but should be used for joins - make private once used
- public static class MultiTableColumnResolver extends BaseColumnResolver implements TableNodeVisitor {
+ public static class MultiTableColumnResolver extends BaseColumnResolver implements TableNodeVisitor<Void> {
private final ListMultimap<String, TableRef> tableMap;
private final List<TableRef> tables;
@@ -353,17 +353,19 @@ public class FromCompiler {
}
@Override
- public void visit(BindTableNode boundTableNode) throws SQLException {
+ public Void visit(BindTableNode boundTableNode) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
@Override
- public void visit(JoinTableNode joinNode) throws SQLException {
- joinNode.getTable().accept(this);
+ public Void visit(JoinTableNode joinNode) throws SQLException {
+ joinNode.getLHS().accept(this);
+ joinNode.getRHS().accept(this);
+ return null;
}
@Override
- public void visit(NamedTableNode tableNode) throws SQLException {
+ public Void visit(NamedTableNode tableNode) throws SQLException {
String alias = tableNode.getAlias();
TableRef tableRef = createTableRef(tableNode, true);
PTable theTable = tableRef.getTable();
@@ -378,10 +380,11 @@ public class FromCompiler {
tableMap.put(name, tableRef);
}
tables.add(tableRef);
+ return null;
}
@Override
- public void visit(DerivedTableNode subselectNode) throws SQLException {
+ public Void visit(DerivedTableNode subselectNode) throws SQLException {
throw new SQLFeatureNotSupportedException();
}
[2/3] PHOENIX-71 Support sub-joins
Posted by ma...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-phoenix/blob/12bd7aed/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
index fd5174b..6136e4e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/JoinCompiler.java
@@ -50,7 +50,6 @@ import org.apache.phoenix.parse.CastParseNode;
import org.apache.phoenix.parse.ColumnDef;
import org.apache.phoenix.parse.ColumnParseNode;
import org.apache.phoenix.parse.ComparisonParseNode;
-import org.apache.phoenix.parse.ConcreteTableNode;
import org.apache.phoenix.parse.DerivedTableNode;
import org.apache.phoenix.parse.EqualParseNode;
import org.apache.phoenix.parse.FunctionParseNode;
@@ -102,179 +101,229 @@ public class JoinCompiler {
GENERAL,
}
- public static class JoinSpec {
- private ColumnResolver origResolver;
- private TableNode mainTableNode;
- private List<ColumnDef> dynamicColumns;
- private TableRef mainTable;
- private List<AliasedNode> select; // all basic nodes related to mainTable, no aggregation.
- private List<ParseNode> preFilters;
- private List<ParseNode> postFilters;
- private List<JoinTable> joinTables;
- private boolean useStarJoin;
- private Map<TableRef, JoinTable> tableRefToJoinTableMap;
- private Map<ColumnRef, ColumnRefType> columnRefs;
-
- private JoinSpec(SelectStatement statement, ColumnResolver resolver) throws SQLException {
- this.origResolver = resolver;
- List<AliasedNode> selectList = statement.getSelect();
- List<TableNode> tableNodes = statement.getFrom();
- assert (tableNodes.size() > 1);
- Iterator<TableNode> iter = tableNodes.iterator();
- Iterator<TableRef> tableRefIter = resolver.getTables().iterator();
- this.mainTableNode = iter.next();
- DynamicColumnsVisitor v = new DynamicColumnsVisitor();
- this.mainTableNode.accept(v);
- this.dynamicColumns = v.getDynamicColumns();
- this.mainTable = tableRefIter.next();
- this.select = extractFromSelect(selectList, mainTable, resolver);
- this.joinTables = new ArrayList<JoinTable>(tableNodes.size() - 1);
- this.preFilters = new ArrayList<ParseNode>();
- this.postFilters = new ArrayList<ParseNode>();
- this.useStarJoin = !statement.getHint().hasHint(Hint.NO_STAR_JOIN);
- this.tableRefToJoinTableMap = new HashMap<TableRef, JoinTable>();
- ColumnParseNodeVisitor generalRefVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor joinLocalRefVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor prefilterRefVisitor = new ColumnParseNodeVisitor(resolver);
- int lastRightJoinIndex = -1;
- TableNode tableNode = null;
- int i = 0;
- while (iter.hasNext()) {
- tableNode = iter.next();
- if (!(tableNode instanceof JoinTableNode))
- throw new SQLFeatureNotSupportedException("Implicit joins not supported.");
- JoinTableNode joinTableNode = (JoinTableNode) tableNode;
- JoinTable joinTable = new JoinTable(joinTableNode, tableRefIter.next(), statement, resolver);
- for (ParseNode condition : joinTable.conditions) {
- ComparisonParseNode comparisonNode = (ComparisonParseNode) condition;
- comparisonNode.getLHS().accept(generalRefVisitor);
- comparisonNode.getRHS().accept(joinLocalRefVisitor);
- }
- if (joinTable.getType() == JoinType.Right) {
- lastRightJoinIndex = i;
- }
- joinTables.add(joinTable);
- tableRefToJoinTableMap.put(joinTable.getTable(), joinTable);
- i++;
- }
- List<TableRef> prefilterAcceptedTables = new ArrayList<TableRef>();
- for (i = lastRightJoinIndex == -1 ? 0 : lastRightJoinIndex; i < joinTables.size(); i++) {
- JoinTable joinTable = joinTables.get(i);
- if (joinTable.getType() != JoinType.Left) {
- prefilterAcceptedTables.add(joinTable.getTable());
- }
- }
- if (statement.getWhere() != null) {
- if (lastRightJoinIndex > -1 && prefilterAcceptedTables.isEmpty()) {
- // conditions can't be pushed down to the scan filter.
- postFilters.add(statement.getWhere());
- } else {
- statement.getWhere().accept(new WhereNodeVisitor(resolver, lastRightJoinIndex > -1, prefilterAcceptedTables));
- for (ParseNode prefilter : preFilters) {
- prefilter.accept(prefilterRefVisitor);
- }
- }
- for (ParseNode postfilter : postFilters) {
- postfilter.accept(generalRefVisitor);
- }
- }
- // Delayed to this point, since pre-filters might have been post-fixed by WhereNodeVisitor.
- for (JoinTable joinTable : joinTables) {
- for (ParseNode prefilter : joinTable.preFilters) {
- prefilter.accept(prefilterRefVisitor);
- }
+ private final SelectStatement statement;
+ private final ColumnResolver origResolver;
+ private final boolean useStarJoin;
+ private final Map<ColumnRef, ColumnRefType> columnRefs;
+
+
+ private JoinCompiler(SelectStatement statement, ColumnResolver resolver) {
+ this.statement = statement;
+ this.origResolver = resolver;
+ this.useStarJoin = !statement.getHint().hasHint(Hint.NO_STAR_JOIN);
+ this.columnRefs = new HashMap<ColumnRef, ColumnRefType>();
+ }
+
+ public static JoinTable compile(SelectStatement statement, ColumnResolver resolver) throws SQLException {
+ JoinCompiler compiler = new JoinCompiler(statement, resolver);
+
+ List<TableNode> from = statement.getFrom();
+ if (from.size() > 1) {
+ throw new SQLFeatureNotSupportedException("Cross join not supported.");
+ }
+
+ JoinTableConstructor constructor = compiler.new JoinTableConstructor();
+ Pair<Table, List<JoinSpec>> res = from.get(0).accept(constructor);
+ JoinTable joinTable = res.getSecond() == null ? compiler.new JoinTable(res.getFirst()) : compiler.new JoinTable(res.getFirst(), res.getSecond());
+ if (statement.getWhere() != null) {
+ joinTable.addFilter(statement.getWhere());
+ }
+
+ ColumnParseNodeVisitor generalRefVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor joinLocalRefVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor prefilterRefVisitor = new ColumnParseNodeVisitor(resolver);
+
+ joinTable.pushDownColumnRefVisitors(generalRefVisitor, joinLocalRefVisitor, prefilterRefVisitor);
+
+ for (AliasedNode node : statement.getSelect()) {
+ node.getNode().accept(generalRefVisitor);
+ }
+ if (statement.getGroupBy() != null) {
+ for (ParseNode node : statement.getGroupBy()) {
+ node.accept(generalRefVisitor);
}
- for (AliasedNode node : selectList) {
+ }
+ if (statement.getHaving() != null) {
+ statement.getHaving().accept(generalRefVisitor);
+ }
+ if (statement.getOrderBy() != null) {
+ for (OrderByNode node : statement.getOrderBy()) {
node.getNode().accept(generalRefVisitor);
}
- if (statement.getGroupBy() != null) {
- for (ParseNode node : statement.getGroupBy()) {
- node.accept(generalRefVisitor);
+ }
+
+ for (ColumnRef ref : generalRefVisitor.getColumnRefMap().keySet()) {
+ compiler.columnRefs.put(ref, ColumnRefType.GENERAL);
+ }
+ for (ColumnRef ref : joinLocalRefVisitor.getColumnRefMap().keySet()) {
+ if (!compiler.columnRefs.containsKey(ref))
+ compiler.columnRefs.put(ref, ColumnRefType.JOINLOCAL);
+ }
+ for (ColumnRef ref : prefilterRefVisitor.getColumnRefMap().keySet()) {
+ if (!compiler.columnRefs.containsKey(ref))
+ compiler.columnRefs.put(ref, ColumnRefType.PREFILTER);
+ }
+
+ return joinTable;
+ }
+
+ private class JoinTableConstructor implements TableNodeVisitor<Pair<Table, List<JoinSpec>>> {
+
+ private TableRef resolveTable(String alias, TableName name) throws SQLException {
+ if (alias != null)
+ return origResolver.resolveTable(null, alias);
+
+ return origResolver.resolveTable(name.getSchemaName(), name.getTableName());
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(BindTableNode boundTableNode) throws SQLException {
+ TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
+ List<AliasedNode> selectNodes = extractFromSelect(statement.getSelect(), tableRef, origResolver);
+ Table table = new Table(boundTableNode, Collections.<ColumnDef>emptyList(), selectNodes, tableRef);
+ return new Pair<Table, List<JoinSpec>>(table, null);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(JoinTableNode joinNode) throws SQLException {
+ Pair<Table, List<JoinSpec>> lhs = joinNode.getLHS().accept(this);
+ Pair<Table, List<JoinSpec>> rhs = joinNode.getRHS().accept(this);
+ JoinTable joinTable = rhs.getSecond() == null ? new JoinTable(rhs.getFirst()) : new JoinTable(rhs.getFirst(), rhs.getSecond());
+ List<JoinSpec> joinSpecs = lhs.getSecond();
+ if (joinSpecs == null) {
+ joinSpecs = new ArrayList<JoinSpec>();
+ }
+ joinSpecs.add(new JoinSpec(joinNode.getType(), joinNode.getOnNode(), joinTable, origResolver));
+
+ return new Pair<Table, List<JoinSpec>>(lhs.getFirst(), joinSpecs);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(NamedTableNode namedTableNode)
+ throws SQLException {
+ TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
+ List<AliasedNode> selectNodes = extractFromSelect(statement.getSelect(), tableRef, origResolver);
+ Table table = new Table(namedTableNode, namedTableNode.getDynamicColumns(), selectNodes, tableRef);
+ return new Pair<Table, List<JoinSpec>>(table, null);
+ }
+
+ @Override
+ public Pair<Table, List<JoinSpec>> visit(DerivedTableNode subselectNode)
+ throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+ }
+
+ public class JoinTable {
+ private final Table table;
+ private final List<JoinSpec> joinSpecs;
+ private final List<ParseNode> postFilters;
+ private final List<Table> tables;
+ private final List<TableRef> tableRefs;
+ private final boolean hasRightJoin;
+ private final List<JoinTable> prefilterAcceptedTables;
+
+ private JoinTable(Table table) {
+ this.table = table;
+ this.joinSpecs = Collections.<JoinSpec>emptyList();
+ this.postFilters = Collections.<ParseNode>emptyList();
+ this.tables = Collections.<Table>singletonList(table);
+ this.tableRefs = Collections.<TableRef>singletonList(table.getTableRef());
+ this.hasRightJoin = false;
+ this.prefilterAcceptedTables = Collections.<JoinTable>emptyList();
+ }
+
+ private JoinTable(Table table, List<JoinSpec> joinSpecs) {
+ this.table = table;
+ this.joinSpecs = joinSpecs;
+ this.postFilters = new ArrayList<ParseNode>();
+ this.tables = new ArrayList<Table>();
+ this.tableRefs = new ArrayList<TableRef>();
+ this.tables.add(table);
+ int lastRightJoinIndex = -1;
+ for (int i = 0; i < joinSpecs.size(); i++) {
+ this.tables.addAll(joinSpecs.get(i).getJoinTable().getTables());
+ if (joinSpecs.get(i).getType() == JoinType.Right) {
+ lastRightJoinIndex = i;
}
}
- if (statement.getHaving() != null) {
- statement.getHaving().accept(generalRefVisitor);
+ for (Table t : this.tables) {
+ this.tableRefs.add(t.getTableRef());
}
- if (statement.getOrderBy() != null) {
- for (OrderByNode node : statement.getOrderBy()) {
- node.getNode().accept(generalRefVisitor);
+ this.hasRightJoin = lastRightJoinIndex > -1;
+ this.prefilterAcceptedTables = new ArrayList<JoinTable>();
+ for (int i = lastRightJoinIndex == -1 ? 0 : lastRightJoinIndex; i < joinSpecs.size(); i++) {
+ JoinSpec joinSpec = joinSpecs.get(i);
+ if (joinSpec.getType() != JoinType.Left) {
+ prefilterAcceptedTables.add(joinSpec.getJoinTable());
}
}
- this.columnRefs = new HashMap<ColumnRef, ColumnRefType>();
- for (ColumnRef ref : generalRefVisitor.getColumnRefMap().keySet()) {
- columnRefs.put(ref, ColumnRefType.GENERAL);
- }
- for (ColumnRef ref : joinLocalRefVisitor.getColumnRefMap().keySet()) {
- if (!columnRefs.containsKey(ref))
- columnRefs.put(ref, ColumnRefType.JOINLOCAL);
- }
- for (ColumnRef ref : prefilterRefVisitor.getColumnRefMap().keySet()) {
- if (!columnRefs.containsKey(ref))
- columnRefs.put(ref, ColumnRefType.PREFILTER);
- }
}
- private JoinSpec(ColumnResolver resolver, TableNode tableNode, List<ColumnDef> dynamicColumns, TableRef table, List<AliasedNode> select, List<ParseNode> preFilters,
- List<ParseNode> postFilters, List<JoinTable> joinTables, boolean useStarJoin, Map<TableRef, JoinTable> tableRefToJoinTableMap, Map<ColumnRef, ColumnRefType> columnRefs) {
- this.origResolver = resolver;
- this.mainTableNode = tableNode;
- this.dynamicColumns = dynamicColumns;
- this.mainTable = table;
- this.select = select;
- this.preFilters = preFilters;
- this.postFilters = postFilters;
- this.joinTables = joinTables;
- this.useStarJoin = useStarJoin;
- this.tableRefToJoinTableMap = tableRefToJoinTableMap;
- this.columnRefs = columnRefs;
+ public Table getTable() {
+ return table;
}
- public ColumnResolver getOriginalResolver() {
- return origResolver;
+ public List<JoinSpec> getJoinSpecs() {
+ return joinSpecs;
}
- public TableNode getMainTableNode() {
- return mainTableNode;
+ public List<Table> getTables() {
+ return tables;
}
- public List<ColumnDef> getDynamicColumns() {
- return dynamicColumns;
+ public List<TableRef> getTableRefs() {
+ return tableRefs;
}
- public TableRef getMainTable() {
- return mainTable;
+ public ColumnResolver getOriginalResolver() {
+ return origResolver;
}
- public List<AliasedNode> getSelect() {
- return select;
+ public Map<ColumnRef, ColumnRefType> getColumnRefs() {
+ return columnRefs;
}
- public List<ParseNode> getPreFilters() {
- return preFilters;
- }
-
- public List<ParseNode> getPostFilters() {
- return postFilters;
+ public void addFilter(ParseNode filter) throws SQLException {
+ if (joinSpecs.isEmpty()) {
+ table.getPreFilters().add(filter);
+ return;
+ }
+
+ WhereNodeVisitor visitor = new WhereNodeVisitor(origResolver, table.getPreFilters(),
+ postFilters, Collections.<TableRef>singletonList(table.getTableRef()),
+ hasRightJoin, prefilterAcceptedTables);
+ filter.accept(visitor);
}
- public List<JoinTable> getJoinTables() {
- return joinTables;
+ public void pushDownColumnRefVisitors(ColumnParseNodeVisitor generalRefVisitor,
+ ColumnParseNodeVisitor joinLocalRefVisitor,
+ ColumnParseNodeVisitor prefilterRefVisitor) throws SQLException {
+ for (ParseNode node : table.getPreFilters()) {
+ node.accept(prefilterRefVisitor);
+ }
+ for (ParseNode node : postFilters) {
+ node.accept(generalRefVisitor);
+ }
+ for (JoinSpec joinSpec : joinSpecs) {
+ JoinTable joinTable = joinSpec.getJoinTable();
+ boolean hasSubJoin = !joinTable.getJoinSpecs().isEmpty();
+ for (ComparisonParseNode node : joinSpec.getOnConditions()) {
+ node.getLHS().accept(generalRefVisitor);
+ if (hasSubJoin) {
+ node.getRHS().accept(generalRefVisitor);
+ } else {
+ node.getRHS().accept(joinLocalRefVisitor);
+ }
+ }
+ joinTable.pushDownColumnRefVisitors(generalRefVisitor, joinLocalRefVisitor, prefilterRefVisitor);
+ }
}
- public ParseNode getPreFiltersCombined() {
- if (preFilters == null || preFilters.isEmpty())
+ public Expression compilePostFilterExpression(StatementContext context) throws SQLException {
+ if (postFilters == null || postFilters.isEmpty())
return null;
- if (preFilters.size() == 1)
- return preFilters.get(0);
-
- return NODE_FACTORY.and(preFilters);
- }
-
- public Expression compilePostFilterExpression(StatementContext context) throws SQLException {
- if (postFilters == null || postFilters.isEmpty())
- return null;
-
ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
List<Expression> expressions = new ArrayList<Expression>(postFilters.size());
for (ParseNode postFilter : postFilters) {
@@ -284,7 +333,7 @@ public class JoinCompiler {
}
if (expressions.size() == 1)
- return expressions.get(0);
+ return expressions.get(0);
return AndExpression.create(expressions);
}
@@ -297,25 +346,23 @@ public class JoinCompiler {
* @return a boolean vector for a star join; or null for non star join.
*/
public boolean[] getStarJoinVector() {
- assert(!joinTables.isEmpty());
-
- int count = joinTables.size();
+ int count = joinSpecs.size();
if (!useStarJoin
&& count > 1
- && joinTables.get(count - 1).getType() != JoinType.Left)
+ && joinSpecs.get(count - 1).getType() != JoinType.Left)
return null;
boolean[] vector = new boolean[count];
for (int i = 0; i < count; i++) {
- JoinTable joinTable = joinTables.get(i);
- if (joinTable.getType() != JoinType.Left
- && joinTable.getType() != JoinType.Inner)
+ JoinSpec joinSpec = joinSpecs.get(i);
+ if (joinSpec.getType() != JoinType.Left
+ && joinSpec.getType() != JoinType.Inner)
return null;
vector[i] = true;
- Iterator<TableRef> iter = joinTable.getLeftTableRefs().iterator();
+ Iterator<TableRef> iter = joinSpec.getDependencies().iterator();
while (vector[i] == true && iter.hasNext()) {
TableRef tableRef = iter.next();
- if (!tableRef.equals(mainTable)) {
+ if (!tableRef.equals(table.getTableRef())) {
vector[i] = false;
}
}
@@ -324,328 +371,83 @@ public class JoinCompiler {
return vector;
}
- protected boolean isWildCardSelect(TableRef table) {
- List<AliasedNode> selectList = table.equals(mainTable) ? this.select : tableRefToJoinTableMap.get(table).getSelect();
- return (selectList.size() == 1 && selectList.get(0).getNode() instanceof TableWildcardParseNode);
- }
-
- public void projectColumns(Scan scan, TableRef table) {
- if (isWildCardSelect(table)) {
- scan.getFamilyMap().clear();
- return;
- }
- for (ColumnRef columnRef : columnRefs.keySet()) {
- if (columnRef.getTableRef().equals(table)
- && !SchemaUtil.isPKColumn(columnRef.getColumn())) {
- scan.addColumn(columnRef.getColumn().getFamilyName().getBytes(), columnRef.getColumn().getName().getBytes());
- }
- }
- }
-
- public ProjectedPTableWrapper createProjectedTable(TableRef tableRef, boolean retainPKColumns) throws SQLException {
- List<PColumn> projectedColumns = new ArrayList<PColumn>();
- List<Expression> sourceExpressions = new ArrayList<Expression>();
- ListMultimap<String, String> columnNameMap = ArrayListMultimap.<String, String>create();
- PTable table = tableRef.getTable();
- boolean hasSaltingColumn = retainPKColumns && table.getBucketNum() != null;
- if (retainPKColumns) {
- for (PColumn column : table.getPKColumns()) {
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, column.getFamilyName(), hasSaltingColumn);
- }
- }
- if (isWildCardSelect(tableRef)) {
- for (PColumn column : table.getColumns()) {
- if (!retainPKColumns || !SchemaUtil.isPKColumn(column)) {
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
- }
- }
- } else {
- for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
- ColumnRef columnRef = e.getKey();
- if (e.getValue() != ColumnRefType.PREFILTER
- && columnRef.getTableRef().equals(tableRef)
- && (!retainPKColumns || !SchemaUtil.isPKColumn(columnRef.getColumn()))) {
- PColumn column = columnRef.getColumn();
- addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
- column, tableRef, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
- }
- }
- }
-
- PTable t = PTableImpl.makePTable(table.getTenantId(), PNameFactory.newName(PROJECTED_TABLE_SCHEMA), table.getName(), PTableType.JOIN,
- table.getIndexState(), table.getTimeStamp(), table.getSequenceNumber(), table.getPKName(),
- retainPKColumns ? table.getBucketNum() : null, projectedColumns, table.getParentTableName(),
- table.getIndexes(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getViewType(), table.getViewIndexId());
- return new ProjectedPTableWrapper(t, columnNameMap, sourceExpressions);
+ public JoinTable getSubJoinTableWithoutPostFilters() {
+ return joinSpecs.size() > 1 ? new JoinTable(table, joinSpecs.subList(0, joinSpecs.size() - 1)) :
+ new JoinTable(table);
}
- private static void addProjectedColumn(List<PColumn> projectedColumns, List<Expression> sourceExpressions,
- ListMultimap<String, String> columnNameMap, PColumn sourceColumn, TableRef sourceTable, PName familyName, boolean hasSaltingColumn)
- throws SQLException {
- if (sourceColumn == SALTING_COLUMN)
- return;
+ public SelectStatement getSubqueryWithoutJoin(boolean asSubquery) {
+ if (asSubquery)
+ return table.getAsSubquery();
- int position = projectedColumns.size() + (hasSaltingColumn ? 1 : 0);
- PTable table = sourceTable.getTable();
- String schemaName = table.getSchemaName().getString();
- String tableName = table.getTableName().getString();
- String colName = sourceColumn.getName().getString();
- String fullName = getProjectedColumnName(schemaName, tableName, colName);
- String aliasedName = sourceTable.getTableAlias() == null ? fullName : getProjectedColumnName(null, sourceTable.getTableAlias(), colName);
-
- columnNameMap.put(colName, aliasedName);
- if (!fullName.equals(aliasedName)) {
- columnNameMap.put(fullName, aliasedName);
- }
-
- PName name = PNameFactory.newName(aliasedName);
- PColumnImpl column = new PColumnImpl(name, familyName, sourceColumn.getDataType(),
- sourceColumn.getMaxLength(), sourceColumn.getScale(), sourceColumn.isNullable(),
- position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced());
- Expression sourceExpression = new ColumnRef(sourceTable, sourceColumn.getPosition()).newColumnExpression();
- projectedColumns.add(column);
- sourceExpressions.add(sourceExpression);
- }
-
- public ColumnResolver getColumnResolver(PTableWrapper table) {
- return new JoinedTableColumnResolver(table, origResolver);
+ return NODE_FACTORY.select(Collections.<TableNode>singletonList(table.getTableNode()), statement.getHint(), statement.isDistinct(), statement.getSelect(), table.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
}
- public boolean hasPostReference(TableRef table) {
- if (isWildCardSelect(table))
- return true;
-
- for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
- if (e.getValue() == ColumnRefType.GENERAL && e.getKey().getTableRef().equals(table)) {
+ public boolean hasPostReference() {
+ for (Table table : tables) {
+ if (table.isWildCardSelect()) {
return true;
}
}
- return false;
- }
-
- private class WhereNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
- private ColumnResolver resolver;
- private boolean hasRightJoin;
- private List<TableRef> prefilterAcceptedTables;
-
- public WhereNodeVisitor(ColumnResolver resolver, boolean hasRightJoin, List<TableRef> prefilterAcceptedTables) {
- this.resolver = resolver;
- this.hasRightJoin = hasRightJoin;
- this.prefilterAcceptedTables = prefilterAcceptedTables;
- }
-
- private Void leaveBooleanNode(ParseNode node,
- List<Void> l) throws SQLException {
- ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
- node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(mainTable);
- switch (type) {
- case NONE:
- case SELF_ONLY:
- if (!hasRightJoin) {
- preFilters.add(node);
- } else {
- postFilters.add(node);
- }
- break;
- case FOREIGN_ONLY:
- TableRef matched = null;
- for (TableRef table : prefilterAcceptedTables) {
- if (visitor.getContentType(table) == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- matched = table;
- break;
- }
- }
- if (matched != null) {
- tableRefToJoinTableMap.get(matched).preFilters.add(node);
- } else {
- postFilters.add(node);
- }
- break;
- default:
- postFilters.add(node);
- break;
+ for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
+ if (e.getValue() == ColumnRefType.GENERAL && tableRefs.contains(e.getKey().getTableRef())) {
+ return true;
}
- return null;
- }
-
- @Override
- public Void visitLeave(LikeParseNode node,
- List<Void> l) throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public boolean visitEnter(AndParseNode node) {
- return true;
}
- @Override
- public Void visitLeave(OrParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(ComparisonParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(NotParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(InListParseNode node,
- List<Void> l) throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(IsNullParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(FunctionParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(BetweenParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(CaseParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
-
- @Override
- public Void visitLeave(CastParseNode node, List<Void> l)
- throws SQLException {
- return leaveBooleanNode(node, l);
- }
+ return false;
}
}
- public static JoinSpec getSubJoinSpecWithoutPostFilters(JoinSpec join) {
- return new JoinSpec(join.origResolver, join.mainTableNode, join.dynamicColumns, join.mainTable, join.select, join.preFilters, new ArrayList<ParseNode>(),
- join.joinTables.subList(0, join.joinTables.size() - 1), join.useStarJoin, join.tableRefToJoinTableMap, join.columnRefs);
- }
-
- public static class JoinTable {
- private JoinType type;
- private TableNode tableNode; // original table node
- private List<ColumnDef> dynamicColumns;
- private TableRef table;
- private List<AliasedNode> select; // all basic nodes related to this table, no aggregation.
- private HintNode hint;
- private List<ParseNode> preFilters;
- private List<ParseNode> conditions;
- private SelectStatement subquery;
-
- private Set<TableRef> leftTableRefs;
+ public static class JoinSpec {
+ private final JoinType type;
+ private final List<ComparisonParseNode> onConditions;
+ private final JoinTable joinTable;
+ private Set<TableRef> dependencies;
- public JoinTable(JoinTableNode node, TableRef tableRef, SelectStatement statement, ColumnResolver resolver) throws SQLException {
- if (!(node.getTable() instanceof ConcreteTableNode))
- throw new SQLFeatureNotSupportedException("Subqueries not supported.");
-
- this.type = node.getType();
- this.tableNode = node.getTable();
- DynamicColumnsVisitor v = new DynamicColumnsVisitor();
- this.tableNode.accept(v);
- this.dynamicColumns = v.getDynamicColumns();
- this.table = tableRef;
- this.select = extractFromSelect(statement.getSelect(),tableRef,resolver);
- this.hint = statement.getHint();
- this.preFilters = new ArrayList<ParseNode>();
- this.conditions = new ArrayList<ParseNode>();
- this.leftTableRefs = new HashSet<TableRef>();
- node.getOnNode().accept(new OnNodeVisitor(resolver));
+ private JoinSpec(JoinType type, ParseNode onNode, JoinTable joinTable,
+ ColumnResolver resolver) throws SQLException {
+ this.type = type;
+ this.onConditions = new ArrayList<ComparisonParseNode>();
+ this.joinTable = joinTable;
+ this.dependencies = new HashSet<TableRef>();
+ OnNodeVisitor visitor = new OnNodeVisitor(resolver, onConditions, dependencies, joinTable);
+ onNode.accept(visitor);
}
public JoinType getType() {
return type;
}
- public TableNode getTableNode() {
- return tableNode;
+ public List<ComparisonParseNode> getOnConditions() {
+ return onConditions;
}
- public List<ColumnDef> getDynamicColumns() {
- return dynamicColumns;
+ public JoinTable getJoinTable() {
+ return joinTable;
}
- public TableRef getTable() {
- return table;
- }
-
- public List<AliasedNode> getSelect() {
- return select;
- }
-
- public List<ParseNode> getPreFilters() {
- return preFilters;
- }
-
- public List<ParseNode> getJoinConditions() {
- return conditions;
- }
-
- public SelectStatement getSubquery() {
- return subquery;
- }
-
- public Set<TableRef> getLeftTableRefs() {
- return leftTableRefs;
- }
-
- public ParseNode getPreFiltersCombined() {
- if (preFilters == null || preFilters.isEmpty())
- return null;
-
- if (preFilters.size() == 1)
- return preFilters.get(0);
-
- return NODE_FACTORY.and(preFilters);
- }
-
- public SelectStatement getAsSubquery() {
- if (subquery != null)
- return subquery;
-
- List<TableNode> from = new ArrayList<TableNode>(1);
- from.add(tableNode);
- return NODE_FACTORY.select(from, hint, false, select, getPreFiltersCombined(), null, null, null, null, 0, false);
+ public Set<TableRef> getDependencies() {
+ return dependencies;
}
public Pair<List<Expression>, List<Expression>> compileJoinConditions(StatementContext context, ColumnResolver leftResolver, ColumnResolver rightResolver) throws SQLException {
- ColumnResolver resolver = context.getResolver();
- List<Pair<Expression, Expression>> compiled = new ArrayList<Pair<Expression, Expression>>(conditions.size());
- context.setResolver(leftResolver);
+ ColumnResolver resolver = context.getResolver();
+ List<Pair<Expression, Expression>> compiled = new ArrayList<Pair<Expression, Expression>>(onConditions.size());
+ context.setResolver(leftResolver);
ExpressionCompiler expressionCompiler = new ExpressionCompiler(context);
- for (ParseNode condition : conditions) {
+ for (ParseNode condition : onConditions) {
assert (condition instanceof EqualParseNode);
EqualParseNode equalNode = (EqualParseNode) condition;
expressionCompiler.reset();
Expression left = equalNode.getLHS().accept(expressionCompiler);
compiled.add(new Pair<Expression, Expression>(left, null));
}
- context.setResolver(rightResolver);
+ context.setResolver(rightResolver);
expressionCompiler = new ExpressionCompiler(context);
Iterator<Pair<Expression, Expression>> iter = compiled.iterator();
- for (ParseNode condition : conditions) {
+ for (ParseNode condition : onConditions) {
Pair<Expression, Expression> p = iter.next();
EqualParseNode equalNode = (EqualParseNode) condition;
expressionCompiler.reset();
@@ -757,127 +559,337 @@ public class JoinCompiler {
return PDataType.VARBINARY;
}
+ }
+
+ public class Table {
+ private final TableNode tableNode;
+ private final List<ColumnDef> dynamicColumns;
+ private final TableRef tableRef;
+ private final List<AliasedNode> selectNodes; // all basic nodes related to this table, no aggregation.
+ private final List<ParseNode> preFilters;
+
+ private Table(TableNode tableNode, List<ColumnDef> dynamicColumns,
+ List<AliasedNode> selectNodes, TableRef tableRef) {
+ this.tableNode = tableNode;
+ this.dynamicColumns = dynamicColumns;
+ this.tableRef = tableRef;
+ this.selectNodes = selectNodes;
+ this.preFilters = new ArrayList<ParseNode>();
+ }
+
+ public TableNode getTableNode() {
+ return tableNode;
+ }
+
+ public List<ColumnDef> getDynamicColumns() {
+ return dynamicColumns;
+ }
+
+ public List<AliasedNode> getSelectNodes() {
+ return selectNodes;
+ }
+
+ public List<ParseNode> getPreFilters() {
+ return preFilters;
+ }
- private class OnNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
- private ColumnResolver resolver;
+ public TableRef getTableRef() {
+ return tableRef;
+ }
+
+ public ParseNode getPreFiltersCombined() {
+ if (preFilters == null || preFilters.isEmpty())
+ return null;
- public OnNodeVisitor(ColumnResolver resolver) {
- this.resolver = resolver;
- }
+ if (preFilters.size() == 1)
+ return preFilters.get(0);
- private Void leaveNonEqBooleanNode(ParseNode node,
- List<Void> l) throws SQLException {
- ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
- node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(table);
- if (type == ColumnParseNodeVisitor.ContentType.NONE
- || type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- preFilters.add(node);
- } else {
- throwUnsupportedJoinConditionException();
- }
- return null;
- }
-
- @Override
- public Void visitLeave(LikeParseNode node,
- List<Void> l) throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ return NODE_FACTORY.and(preFilters);
+ }
+
+ public SelectStatement getAsSubquery() {
+ // TODO handle DerivedTableNode differently?
+ List<TableNode> from = Collections.<TableNode>singletonList(tableNode);
+ return NODE_FACTORY.select(from, statement.getHint(), false, selectNodes, getPreFiltersCombined(), null, null, null, null, 0, false);
+ }
+
+ protected boolean isWildCardSelect() {
+ return (selectNodes.size() == 1 && selectNodes.get(0).getNode() instanceof TableWildcardParseNode);
+ }
- @Override
- public boolean visitEnter(AndParseNode node) {
- return true;
- }
-
- @Override
- public Void visitLeave(OrParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ public void projectColumns(Scan scan) {
+ if (isWildCardSelect()) {
+ scan.getFamilyMap().clear();
+ return;
}
-
- @Override
- public Void visitLeave(ComparisonParseNode node, List<Void> l)
- throws SQLException {
- if (!(node instanceof EqualParseNode))
- return leaveNonEqBooleanNode(node, l);
- ColumnParseNodeVisitor lhsVisitor = new ColumnParseNodeVisitor(resolver);
- ColumnParseNodeVisitor rhsVisitor = new ColumnParseNodeVisitor(resolver);
- node.getLHS().accept(lhsVisitor);
- node.getRHS().accept(rhsVisitor);
- ColumnParseNodeVisitor.ContentType lhsType = lhsVisitor.getContentType(table);
- ColumnParseNodeVisitor.ContentType rhsType = rhsVisitor.getContentType(table);
- if ((lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || lhsType == ColumnParseNodeVisitor.ContentType.NONE)
- && (rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || rhsType == ColumnParseNodeVisitor.ContentType.NONE)) {
- preFilters.add(node);
- } else if (lhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
- && rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- conditions.add(node);
- leftTableRefs.addAll(lhsVisitor.getTableRefSet());
- } else if (rhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
- && lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
- conditions.add(NODE_FACTORY.equal(node.getRHS(), node.getLHS()));
- leftTableRefs.addAll(rhsVisitor.getTableRefSet());
- } else {
- throwUnsupportedJoinConditionException();
+ for (ColumnRef columnRef : columnRefs.keySet()) {
+ if (columnRef.getTableRef().equals(tableRef)
+ && !SchemaUtil.isPKColumn(columnRef.getColumn())) {
+ scan.addColumn(columnRef.getColumn().getFamilyName().getBytes(), columnRef.getColumn().getName().getBytes());
}
- return null;
}
-
- @Override
- public Void visitLeave(NotParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ }
+
+ public ProjectedPTableWrapper createProjectedTable(boolean retainPKColumns) throws SQLException {
+ List<PColumn> projectedColumns = new ArrayList<PColumn>();
+ List<Expression> sourceExpressions = new ArrayList<Expression>();
+ ListMultimap<String, String> columnNameMap = ArrayListMultimap.<String, String>create();
+ PTable table = tableRef.getTable();
+ boolean hasSaltingColumn = retainPKColumns && table.getBucketNum() != null;
+ if (retainPKColumns) {
+ for (PColumn column : table.getPKColumns()) {
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, column.getFamilyName(), hasSaltingColumn);
+ }
}
-
- @Override
- public Void visitLeave(InListParseNode node,
- List<Void> l) throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ if (isWildCardSelect()) {
+ for (PColumn column : table.getColumns()) {
+ if (!retainPKColumns || !SchemaUtil.isPKColumn(column)) {
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
+ }
+ }
+ } else {
+ for (Map.Entry<ColumnRef, ColumnRefType> e : columnRefs.entrySet()) {
+ ColumnRef columnRef = e.getKey();
+ if (e.getValue() != ColumnRefType.PREFILTER
+ && columnRef.getTableRef().equals(tableRef)
+ && (!retainPKColumns || !SchemaUtil.isPKColumn(columnRef.getColumn()))) {
+ PColumn column = columnRef.getColumn();
+ addProjectedColumn(projectedColumns, sourceExpressions, columnNameMap,
+ column, PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), hasSaltingColumn);
+ }
+ }
}
- @Override
- public Void visitLeave(IsNullParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ PTable t = PTableImpl.makePTable(table.getTenantId(), PNameFactory.newName(PROJECTED_TABLE_SCHEMA), table.getName(), PTableType.JOIN,
+ table.getIndexState(), table.getTimeStamp(), table.getSequenceNumber(), table.getPKName(),
+ retainPKColumns ? table.getBucketNum() : null, projectedColumns, table.getParentTableName(),
+ table.getIndexes(), table.isImmutableRows(), Collections.<PName>emptyList(), null, null, table.isWALDisabled(), table.isMultiTenant(), table.getViewType(), table.getViewIndexId());
+ return new ProjectedPTableWrapper(t, columnNameMap, sourceExpressions);
+ }
+
+ private void addProjectedColumn(List<PColumn> projectedColumns, List<Expression> sourceExpressions,
+ ListMultimap<String, String> columnNameMap, PColumn sourceColumn, PName familyName, boolean hasSaltingColumn)
+ throws SQLException {
+ if (sourceColumn == SALTING_COLUMN)
+ return;
- @Override
- public Void visitLeave(FunctionParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
- }
+ int position = projectedColumns.size() + (hasSaltingColumn ? 1 : 0);
+ PTable table = tableRef.getTable();
+ String schemaName = table.getSchemaName().getString();
+ String tableName = table.getTableName().getString();
+ String colName = sourceColumn.getName().getString();
+ String fullName = getProjectedColumnName(schemaName, tableName, colName);
+ String aliasedName = tableRef.getTableAlias() == null ? fullName : getProjectedColumnName(null, tableRef.getTableAlias(), colName);
- @Override
- public Void visitLeave(BetweenParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ columnNameMap.put(colName, aliasedName);
+ if (!fullName.equals(aliasedName)) {
+ columnNameMap.put(fullName, aliasedName);
}
- @Override
- public Void visitLeave(CaseParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ PName name = PNameFactory.newName(aliasedName);
+ PColumnImpl column = new PColumnImpl(name, familyName, sourceColumn.getDataType(),
+ sourceColumn.getMaxLength(), sourceColumn.getScale(), sourceColumn.isNullable(),
+ position, sourceColumn.getSortOrder(), sourceColumn.getArraySize(), sourceColumn.getViewConstant(), sourceColumn.isViewReferenced());
+ Expression sourceExpression = new ColumnRef(tableRef, sourceColumn.getPosition()).newColumnExpression();
+ projectedColumns.add(column);
+ sourceExpressions.add(sourceExpression);
+ }
+ }
+
+ private static abstract class ConditionNodeVisitor extends TraverseNoParseNodeVisitor<Void> {
+
+ protected abstract Void leaveBooleanNode(ParseNode node, List<Void> l) throws SQLException;
+
+ @Override
+ public Void visitLeave(LikeParseNode node,
+ List<Void> l) throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public boolean visitEnter(AndParseNode node) {
+ return true;
+ }
+
+ @Override
+ public Void visitLeave(OrParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(ComparisonParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(NotParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(InListParseNode node,
+ List<Void> l) throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(IsNullParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(FunctionParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(BetweenParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(CaseParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+
+ @Override
+ public Void visitLeave(CastParseNode node, List<Void> l)
+ throws SQLException {
+ return leaveBooleanNode(node, l);
+ }
+ }
+
+ private static class WhereNodeVisitor extends ConditionNodeVisitor {
+ private ColumnResolver resolver;
+ private List<ParseNode> preFilters;
+ private List<ParseNode> postFilters;
+ private List<TableRef> selfTableRefs;
+ private boolean hasRightJoin;
+ private List<JoinTable> prefilterAcceptedTables;
+
+ public WhereNodeVisitor(ColumnResolver resolver, List<ParseNode> preFilters,
+ List<ParseNode> postFilters, List<TableRef> selfTableRefs, boolean hasRightJoin,
+ List<JoinTable> prefilterAcceptedTables) {
+ this.resolver = resolver;
+ this.preFilters = preFilters;
+ this.postFilters = postFilters;
+ this.selfTableRefs = selfTableRefs;
+ this.hasRightJoin = hasRightJoin;
+ this.prefilterAcceptedTables = prefilterAcceptedTables;
+ }
+
+ protected Void leaveBooleanNode(ParseNode node,
+ List<Void> l) throws SQLException {
+ ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
+ node.accept(visitor);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(selfTableRefs);
+ switch (type) {
+ case NONE:
+ case SELF_ONLY:
+ if (!hasRightJoin) {
+ preFilters.add(node);
+ } else {
+ postFilters.add(node);
+ }
+ break;
+ case FOREIGN_ONLY:
+ JoinTable matched = null;
+ for (JoinTable joinTable : prefilterAcceptedTables) {
+ if (visitor.getContentType(joinTable.getTableRefs()) == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ matched = joinTable;
+ break;
+ }
+ }
+ if (matched != null) {
+ matched.addFilter(node);
+ } else {
+ postFilters.add(node);
+ }
+ break;
+ default:
+ postFilters.add(node);
+ break;
}
-
- @Override
- public Void visitLeave(CastParseNode node, List<Void> l)
- throws SQLException {
- return leaveNonEqBooleanNode(node, l);
+ return null;
+ }
+ }
+
+ private static class OnNodeVisitor extends ConditionNodeVisitor {
+ private ColumnResolver resolver;
+ private List<ComparisonParseNode> onConditions;
+ private Set<TableRef> dependencies;
+ private JoinTable joinTable;
+
+ public OnNodeVisitor(ColumnResolver resolver, List<ComparisonParseNode> onConditions,
+ Set<TableRef> dependencies, JoinTable joinTable) {
+ this.resolver = resolver;
+ this.onConditions = onConditions;
+ this.dependencies = dependencies;
+ this.joinTable = joinTable;
+ }
+
+ protected Void leaveBooleanNode(ParseNode node,
+ List<Void> l) throws SQLException {
+ ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
+ node.accept(visitor);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(joinTable.getTableRefs());
+ if (type == ColumnParseNodeVisitor.ContentType.NONE
+ || type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ joinTable.addFilter(node);
+ } else {
+ throwUnsupportedJoinConditionException();
}
+ return null;
+ }
- /*
- * Conditions in the ON clause can only be:
- * 1) an equal test between a self table expression and a foreign
- * table expression.
- * 2) a boolean condition referencing to the self table only.
- * Otherwise, it can be ambiguous.
- */
- private void throwUnsupportedJoinConditionException()
- throws SQLFeatureNotSupportedException {
- throw new SQLFeatureNotSupportedException("Does not support non-standard or non-equi join conditions.");
- }
+ @Override
+ public Void visitLeave(ComparisonParseNode node, List<Void> l)
+ throws SQLException {
+ if (!(node instanceof EqualParseNode))
+ return leaveBooleanNode(node, l);
+ ColumnParseNodeVisitor lhsVisitor = new ColumnParseNodeVisitor(resolver);
+ ColumnParseNodeVisitor rhsVisitor = new ColumnParseNodeVisitor(resolver);
+ node.getLHS().accept(lhsVisitor);
+ node.getRHS().accept(rhsVisitor);
+ ColumnParseNodeVisitor.ContentType lhsType = lhsVisitor.getContentType(joinTable.getTableRefs());
+ ColumnParseNodeVisitor.ContentType rhsType = rhsVisitor.getContentType(joinTable.getTableRefs());
+ if ((lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || lhsType == ColumnParseNodeVisitor.ContentType.NONE)
+ && (rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY || rhsType == ColumnParseNodeVisitor.ContentType.NONE)) {
+ joinTable.addFilter(node);
+ } else if (lhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
+ && rhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ onConditions.add(node);
+ dependencies.addAll(lhsVisitor.getTableRefSet());
+ } else if (rhsType == ColumnParseNodeVisitor.ContentType.FOREIGN_ONLY
+ && lhsType == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
+ onConditions.add(NODE_FACTORY.equal(node.getRHS(), node.getLHS()));
+ dependencies.addAll(rhsVisitor.getTableRefSet());
+ } else {
+ throwUnsupportedJoinConditionException();
+ }
+ return null;
}
+
+ /*
+ * Conditions in the ON clause can only be:
+ * 1) an equal test between a self table expression and a foreign
+ * table expression.
+ * 2) a boolean condition referencing to the self table only.
+ * Otherwise, it can be ambiguous.
+ */
+ private void throwUnsupportedJoinConditionException()
+ throws SQLFeatureNotSupportedException {
+ throw new SQLFeatureNotSupportedException("Does not support non-standard or non-equi join conditions.");
+ }
}
private static class ColumnParseNodeVisitor extends StatelessTraverseAllParseNodeVisitor {
@@ -914,14 +926,33 @@ public class JoinCompiler {
return columnRefMap;
}
- public ContentType getContentType(TableRef selfTable) {
+ public ContentType getContentType(List<TableRef> selfTableRefs) {
if (tableRefSet.isEmpty())
return ContentType.NONE;
- if (tableRefSet.size() > 1)
- return ContentType.COMPLEX;
- if (tableRefSet.contains(selfTable))
- return ContentType.SELF_ONLY;
- return ContentType.FOREIGN_ONLY;
+
+ ContentType ret = ContentType.NONE;
+ for (TableRef tRef : tableRefSet) {
+ boolean isSelf = selfTableRefs.contains(tRef);
+ switch (ret) {
+ case NONE:
+ ret = isSelf ? ContentType.SELF_ONLY : ContentType.FOREIGN_ONLY;
+ break;
+ case SELF_ONLY:
+ ret = isSelf ? ContentType.SELF_ONLY : ContentType.COMPLEX;
+ break;
+ case FOREIGN_ONLY:
+ ret = isSelf ? ContentType.COMPLEX : ContentType.FOREIGN_ONLY;
+ break;
+ default: // COMPLEX do nothing
+ break;
+ }
+
+ if (ret == ContentType.COMPLEX) {
+ break;
+ }
+ }
+
+ return ret;
}
}
@@ -929,14 +960,14 @@ public class JoinCompiler {
// for creation of new statements
private static ParseNodeFactory NODE_FACTORY = new ParseNodeFactory();
- private static List<AliasedNode> extractFromSelect(List<AliasedNode> select, TableRef table, ColumnResolver resolver) throws SQLException {
+ private static List<AliasedNode> extractFromSelect(List<AliasedNode> select, TableRef tableRef, ColumnResolver resolver) throws SQLException {
List<AliasedNode> ret = new ArrayList<AliasedNode>();
ColumnParseNodeVisitor visitor = new ColumnParseNodeVisitor(resolver);
for (AliasedNode aliasedNode : select) {
ParseNode node = aliasedNode.getNode();
if (node instanceof TableWildcardParseNode) {
TableName tableName = ((TableWildcardParseNode) node).getTableName();
- if (table.equals(resolver.resolveTable(tableName.getSchemaName(), tableName.getTableName()))) {
+ if (tableRef.equals(resolver.resolveTable(tableName.getSchemaName(), tableName.getTableName()))) {
ret.clear();
ret.add(aliasedNode);
return ret;
@@ -945,12 +976,12 @@ public class JoinCompiler {
}
node.accept(visitor);
- ColumnParseNodeVisitor.ContentType type = visitor.getContentType(table);
+ ColumnParseNodeVisitor.ContentType type = visitor.getContentType(Collections.singletonList(tableRef));
if (type == ColumnParseNodeVisitor.ContentType.SELF_ONLY) {
ret.add(aliasedNode);
} else if (type == ColumnParseNodeVisitor.ContentType.COMPLEX) {
for (Map.Entry<ColumnRef, ColumnParseNode> entry : visitor.getColumnRefMap().entrySet()) {
- if (entry.getKey().getTableRef().equals(table)) {
+ if (entry.getKey().getTableRef().equals(tableRef)) {
ret.add(NODE_FACTORY.aliasedNode(null, entry.getValue()));
}
}
@@ -960,12 +991,8 @@ public class JoinCompiler {
return ret;
}
- public static JoinSpec getJoinSpec(StatementContext context, SelectStatement statement) throws SQLException {
- return new JoinSpec(statement, context.getResolver());
- }
-
public static SelectStatement optimize(StatementContext context, SelectStatement select, PhoenixStatement statement) throws SQLException {
- ColumnResolver resolver = context.getResolver();
+ final ColumnResolver resolver = context.getResolver();
TableRef groupByTableRef = null;
TableRef orderByTableRef = null;
if (select.getGroupBy() != null && !select.getGroupBy().isEmpty()) {
@@ -987,12 +1014,12 @@ public class JoinCompiler {
orderByTableRef = set.iterator().next();
}
}
- JoinSpec join = getJoinSpec(context, select);
+ JoinTable join = compile(select, context.getResolver());
if (groupByTableRef != null || orderByTableRef != null) {
QueryCompiler compiler = new QueryCompiler(statement, select, resolver);
List<Object> binds = statement.getParameters();
StatementContext ctx = new StatementContext(statement, resolver, new Scan());
- QueryPlan plan = compiler.compileJoinQuery(ctx, select, binds, join, false);
+ QueryPlan plan = compiler.compileJoinQuery(ctx, binds, join, false);
TableRef table = plan.getTableRef();
if (groupByTableRef != null && !groupByTableRef.equals(table)) {
groupByTableRef = null;
@@ -1002,94 +1029,81 @@ public class JoinCompiler {
}
}
- Map<TableRef, TableRef> replacement = new HashMap<TableRef, TableRef>();
+ final Map<TableRef, TableRef> replacement = new HashMap<TableRef, TableRef>();
+
+ for (Table table : join.getTables()) {
+ TableRef tableRef = table.getTableRef();
+ List<ParseNode> groupBy = tableRef.equals(groupByTableRef) ? select.getGroupBy() : null;
+ List<OrderByNode> orderBy = tableRef.equals(orderByTableRef) ? select.getOrderBy() : null;
+ SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), table.getDynamicColumns(), tableRef, join.getColumnRefs(), table.getPreFiltersCombined(), groupBy, orderBy, table.isWildCardSelect());
+ QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
+ if (!plan.getTableRef().equals(tableRef)) {
+ replacement.put(tableRef, plan.getTableRef());
+ }
+ }
+
+ if (replacement.isEmpty())
+ return select;
+
List<TableNode> from = select.getFrom();
List<TableNode> newFrom = Lists.newArrayListWithExpectedSize(from.size());
+ for (TableNode node : from) {
+ newFrom.add(node.accept(new TableNodeVisitor<TableNode>() {
+ private TableRef resolveTable(String alias, TableName name) throws SQLException {
+ if (alias != null)
+ return resolver.resolveTable(null, alias);
- class TableNodeRewriter implements TableNodeVisitor {
- private TableRef table;
- private TableNode replaced;
-
- TableNodeRewriter(TableRef table) {
- this.table = table;
- }
-
- public TableNode getReplacedTableNode() {
- return replaced;
- }
+ return resolver.resolveTable(name.getSchemaName(), name.getTableName());
+ }
- @Override
- public void visit(BindTableNode boundTableNode) throws SQLException {
- String alias = boundTableNode.getAlias();
- replaced = NODE_FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName());
- }
+ private TableName getReplacedTableName(TableRef tableRef) {
+ String schemaName = tableRef.getTable().getSchemaName().getString();
+ return TableName.create(schemaName.length() == 0 ? null : schemaName, tableRef.getTable().getTableName().getString());
+ }
- @Override
- public void visit(JoinTableNode joinNode) throws SQLException {
- joinNode.getTable().accept(this);
- replaced = NODE_FACTORY.join(joinNode.getType(), joinNode.getOnNode(), replaced);
- }
+ @Override
+ public TableNode visit(BindTableNode boundTableNode) throws SQLException {
+ TableRef tableRef = resolveTable(boundTableNode.getAlias(), boundTableNode.getName());
+ TableRef replaceRef = replacement.get(tableRef);
+ if (replaceRef == null)
+ return boundTableNode;
- @Override
- public void visit(NamedTableNode namedTableNode)
- throws SQLException {
- String alias = namedTableNode.getAlias();
- replaced = NODE_FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(), namedTableNode.getDynamicColumns());
- }
+ String alias = boundTableNode.getAlias();
+ return NODE_FACTORY.bindTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef));
+ }
- @Override
- public void visit(DerivedTableNode subselectNode)
- throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
-
- private TableName getReplacedTableName() {
- String schemaName = table.getTable().getSchemaName().getString();
- return TableName.create(schemaName.length() == 0 ? null : schemaName, table.getTable().getTableName().getString());
- }
- };
-
- // get optimized plans for join tables
- for (int i = 1; i < from.size(); i++) {
- TableNode jNode = from.get(i);
- assert (jNode instanceof JoinTableNode);
- TableNode tNode = ((JoinTableNode) jNode).getTable();
- for (JoinTable jTable : join.getJoinTables()) {
- if (jTable.getTableNode() != tNode)
- continue;
- TableRef table = jTable.getTable();
- List<ParseNode> groupBy = table.equals(groupByTableRef) ? select.getGroupBy() : null;
- List<OrderByNode> orderBy = table.equals(orderByTableRef) ? select.getOrderBy() : null;
- SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), join.tableRefToJoinTableMap.get(table).getDynamicColumns(), table, join.columnRefs, jTable.getPreFiltersCombined(), groupBy, orderBy, join.isWildCardSelect(table));
- QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
- if (!plan.getTableRef().equals(table)) {
- TableNodeRewriter rewriter = new TableNodeRewriter(plan.getTableRef());
- jNode.accept(rewriter);
- newFrom.add(rewriter.getReplacedTableNode());
- replacement.put(table, plan.getTableRef());
- } else {
- newFrom.add(jNode);
+ @Override
+ public TableNode visit(JoinTableNode joinNode) throws SQLException {
+ TableNode lhs = joinNode.getLHS();
+ TableNode rhs = joinNode.getRHS();
+ TableNode lhsReplace = lhs.accept(this);
+ TableNode rhsReplace = rhs.accept(this);
+ if (lhs == lhsReplace && rhs == rhsReplace)
+ return joinNode;
+
+ return NODE_FACTORY.join(joinNode.getType(), lhsReplace, rhsReplace, joinNode.getOnNode());
}
- }
- }
- // get optimized plan for main table
- TableRef table = join.getMainTable();
- List<ParseNode> groupBy = table.equals(groupByTableRef) ? select.getGroupBy() : null;
- List<OrderByNode> orderBy = table.equals(orderByTableRef) ? select.getOrderBy() : null;
- SelectStatement stmt = getSubqueryForOptimizedPlan(select.getHint(), join.dynamicColumns, table, join.columnRefs, join.getPreFiltersCombined(), groupBy, orderBy, join.isWildCardSelect(table));
- QueryPlan plan = context.getConnection().getQueryServices().getOptimizer().optimize(statement, stmt);
- if (!plan.getTableRef().equals(table)) {
- TableNodeRewriter rewriter = new TableNodeRewriter(plan.getTableRef());
- from.get(0).accept(rewriter);
- newFrom.add(0, rewriter.getReplacedTableNode());
- replacement.put(table, plan.getTableRef());
- } else {
- newFrom.add(0, from.get(0));
+
+ @Override
+ public TableNode visit(NamedTableNode namedTableNode)
+ throws SQLException {
+ TableRef tableRef = resolveTable(namedTableNode.getAlias(), namedTableNode.getName());
+ TableRef replaceRef = replacement.get(tableRef);
+ if (replaceRef == null)
+ return namedTableNode;
+
+ String alias = namedTableNode.getAlias();
+ return NODE_FACTORY.namedTable(alias == null ? null : '"' + alias + '"', getReplacedTableName(replaceRef), namedTableNode.getDynamicColumns());
+ }
+
+ @Override
+ public TableNode visit(DerivedTableNode subselectNode)
+ throws SQLException {
+ throw new SQLFeatureNotSupportedException();
+ }
+ }));
}
- if (replacement.isEmpty())
- return select;
-
return IndexStatementRewriter.translate(NODE_FACTORY.select(select, newFrom), resolver, replacement);
}
@@ -1116,76 +1130,11 @@ public class JoinCompiler {
return NODE_FACTORY.select(from, hintNode, false, selectList, where, groupBy, null, orderBy, null, 0, false);
}
- public static SelectStatement getSubqueryWithoutJoin(SelectStatement statement, JoinSpec join) {
- return NODE_FACTORY.select(statement.getFrom().subList(0, 1), statement.getHint(), statement.isDistinct(), statement.getSelect(), join.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
- }
-
- // Get the last join table select statement with fixed-up select and where nodes.
- // Currently does NOT support last join table as a subquery.
- public static SelectStatement getSubqueryForLastJoinTable(SelectStatement statement, JoinSpec join) throws SQLException {
- List<JoinTable> joinTables = join.getJoinTables();
- int count = joinTables.size();
- assert (count > 0);
- JoinTable lastJoinTable = joinTables.get(count - 1);
- if (lastJoinTable.getSubquery() != null) {
- throw new SQLFeatureNotSupportedException("Subqueries not supported.");
- }
- List<TableNode> from = new ArrayList<TableNode>(1);
- from.add(lastJoinTable.getTableNode());
-
- return NODE_FACTORY.select(from, statement.getHint(), statement.isDistinct(), statement.getSelect(), lastJoinTable.getPreFiltersCombined(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate());
- }
-
- // Get subquery with fixed select and where nodes
- public static SelectStatement getSubQueryWithoutLastJoin(SelectStatement statement, JoinSpec join) {
- List<TableNode> from = statement.getFrom();
- assert(from.size() > 1);
- List<JoinTable> joinTables = join.getJoinTables();
- int count = joinTables.size();
- assert (count > 0);
- List<AliasedNode> select = new ArrayList<AliasedNode>();
- select.addAll(join.getSelect());
- for (int i = 0; i < count - 1; i++) {
- select.addAll(joinTables.get(i).getSelect());
- }
-
- return NODE_FACTORY.select(from.subList(0, from.size() - 1), statement.getHint(), false, select, join.getPreFiltersCombined(), null, null, null, null, statement.getBindCount(), false);
- }
-
- public static PTableWrapper mergeProjectedTables(PTableWrapper lWrapper, PTableWrapper rWrapper, boolean innerJoin) throws SQLException {
- PTable left = lWrapper.getTable();
- PTable right = rWrapper.getTable();
- List<PColumn> merged = new ArrayList<PColumn>();
- merged.addAll(left.getColumns());
- int position = merged.size();
- for (PColumn c : right.getColumns()) {
- if (!SchemaUtil.isPKColumn(c)) {
- PColumnImpl column = new PColumnImpl(c.getName(),
- PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), c.getDataType(),
- c.getMaxLength(), c.getScale(), innerJoin ? c.isNullable() : true, position++,
- c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced());
- merged.add(column);
- }
- }
- if (left.getBucketNum() != null) {
- merged.remove(0);
- }
- PTable t = PTableImpl.makePTable(left.getTenantId(), left.getSchemaName(),
- PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())), left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), left.getBucketNum(), merged,
- left.getParentTableName(), left.getIndexes(), left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getViewType(), left.getViewIndexId());
-
- ListMultimap<String, String> mergedMap = ArrayListMultimap.<String, String>create();
- mergedMap.putAll(lWrapper.getColumnNameMap());
- mergedMap.putAll(rWrapper.getColumnNameMap());
-
- return new PTableWrapper(t, mergedMap);
- }
-
public static ScanProjector getScanProjector(ProjectedPTableWrapper table) {
return new ScanProjector(table);
}
- public static class PTableWrapper {
+ public class PTableWrapper {
protected PTable table;
protected ListMultimap<String, String> columnNameMap;
@@ -1205,9 +1154,42 @@ public class JoinCompiler {
public List<String> getMappedColumnName(String name) {
return columnNameMap.get(name);
}
+
+ public ColumnResolver createColumnResolver() {
+ return new JoinedTableColumnResolver(this, origResolver);
+ }
+
+ public PTableWrapper mergeProjectedTables(PTableWrapper rWrapper, boolean innerJoin) throws SQLException {
+ PTable left = this.getTable();
+ PTable right = rWrapper.getTable();
+ List<PColumn> merged = new ArrayList<PColumn>();
+ merged.addAll(left.getColumns());
+ int position = merged.size();
+ for (PColumn c : right.getColumns()) {
+ if (!SchemaUtil.isPKColumn(c)) {
+ PColumnImpl column = new PColumnImpl(c.getName(),
+ PNameFactory.newName(ScanProjector.VALUE_COLUMN_FAMILY), c.getDataType(),
+ c.getMaxLength(), c.getScale(), innerJoin ? c.isNullable() : true, position++,
+ c.getSortOrder(), c.getArraySize(), c.getViewConstant(), c.isViewReferenced());
+ merged.add(column);
+ }
+ }
+ if (left.getBucketNum() != null) {
+ merged.remove(0);
+ }
+ PTable t = PTableImpl.makePTable(left.getTenantId(), left.getSchemaName(),
+ PNameFactory.newName(SchemaUtil.getTableName(left.getName().getString(), right.getName().getString())), left.getType(), left.getIndexState(), left.getTimeStamp(), left.getSequenceNumber(), left.getPKName(), left.getBucketNum(), merged,
+ left.getParentTableName(), left.getIndexes(), left.isImmutableRows(), Collections.<PName>emptyList(), null, null, PTable.DEFAULT_DISABLE_WAL, left.isMultiTenant(), left.getViewType(), left.getViewIndexId());
+
+ ListMultimap<String, String> mergedMap = ArrayListMultimap.<String, String>create();
+ mergedMap.putAll(this.getColumnNameMap());
+ mergedMap.putAll(rWrapper.getColumnNameMap());
+
+ return new PTableWrapper(t, mergedMap);
+ }
}
- public static class ProjectedPTableWrapper extends PTableWrapper {
+ public class ProjectedPTableWrapper extends PTableWrapper {
private List<Expression> sourceExpressions;
protected ProjectedPTableWrapper(PTable table, ListMultimap<String, String> columnNameMap, List<Expression> sourceExpressions) {
@@ -1274,39 +1256,6 @@ public class JoinCompiler {
return SchemaUtil.getColumnName(SchemaUtil.getTableName(schemaName, tableName), colName);
}
- private static class DynamicColumnsVisitor implements TableNodeVisitor {
- private List<ColumnDef> dynamicCols;
-
- public DynamicColumnsVisitor() {
- }
-
- public List<ColumnDef> getDynamicColumns() {
- return dynamicCols == null ? Collections.<ColumnDef> emptyList() : dynamicCols;
- }
-
- @Override
- public void visit(BindTableNode boundTableNode) throws SQLException {
- }
-
- @Override
- public void visit(JoinTableNode joinNode) throws SQLException {
- // should not expect to see this node.
- assert(false);
- }
-
- @Override
- public void visit(NamedTableNode namedTableNode)
- throws SQLException {
- this.dynamicCols = namedTableNode.getDynamicColumns();
- }
-
- @Override
- public void visit(DerivedTableNode subselectNode)
- throws SQLException {
- throw new SQLFeatureNotSupportedException();
- }
- };
-
}