PHOENIX-1807 Support UNION queries in subquery
Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/9ddb484a Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/9ddb484a Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/9ddb484a Branch: refs/heads/calcite Commit: 9ddb484aaf0b84b729be9b699b0528813b4ffb1b Parents: 7aea692 Author: maryannxue <[email protected]> Authored: Mon Apr 6 23:16:20 2015 -0400 Committer: maryannxue <[email protected]> Committed: Mon Apr 6 23:16:20 2015 -0400 ---------------------------------------------------------------------- .../org/apache/phoenix/end2end/UnionAllIT.java | 91 +++++++++++++++++--- phoenix-core/src/main/antlr3/PhoenixSQL.g | 2 +- .../apache/phoenix/compile/DeleteCompiler.java | 2 +- .../apache/phoenix/compile/FromCompiler.java | 2 +- .../apache/phoenix/compile/JoinCompiler.java | 4 +- .../apache/phoenix/compile/QueryCompiler.java | 4 +- .../phoenix/compile/SubqueryRewriter.java | 19 ++-- .../apache/phoenix/compile/UnionCompiler.java | 6 +- .../apache/phoenix/jdbc/PhoenixStatement.java | 10 +-- .../apache/phoenix/optimize/QueryOptimizer.java | 2 +- .../apache/phoenix/parse/ParseNodeFactory.java | 72 ++++++++++------ .../apache/phoenix/parse/SelectStatement.java | 33 ++++--- 12 files changed, 173 insertions(+), 74 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java index b3b2f7d..1d4055a 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UnionAllIT.java @@ -28,7 +28,6 @@ import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.sql.Statement; import java.util.Collections; import java.util.Map; @@ -424,7 +423,7 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT { } @Test - public void testUnionAllInSubquery() throws Exception { + public void testUnionAllInDerivedTable() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); @@ -435,21 +434,63 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT { " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); + String dml = "UPSERT INTO test_table VALUES(?, ?)"; + PreparedStatement stmt = conn.prepareStatement(dml); + stmt.setString(1, "a"); + stmt.setInt(2, 10); + stmt.execute(); + stmt.setString(1, "b"); + stmt.setInt(2, 20); + stmt.execute(); + conn.commit(); + ddl = "CREATE TABLE b_table " + - " (a_string varchar not null, col1 integer" + + " (a_string varchar not null, col2 integer" + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); - ddl = "select a_string, col1 from test_table where a_string in (select a_string from test_table union all select a_string from b_table)"; - conn.createStatement().executeQuery(ddl); - } catch (SQLFeatureNotSupportedException e) { + dml = "UPSERT INTO b_table VALUES(?, ?)"; + stmt = conn.prepareStatement(dml); + stmt.setString(1, "a"); + stmt.setInt(2, 30); + stmt.execute(); + stmt.setString(1, "c"); + stmt.setInt(2, 60); + stmt.execute(); + conn.commit(); + + String query = "select a_string from " + + "(select a_string, col1 from test_table union all select a_string, col2 from b_table order by a_string)"; + ResultSet rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("a", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("a", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("b", rs.getString(1)); + assertTrue(rs.next()); + assertEquals("c", rs.getString(1)); + assertFalse(rs.next()); + + query = "select c from " + + "(select a_string, col1 c from test_table union all select a_string, col2 c from b_table order by c)"; + rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals(10, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(20, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(30, rs.getInt(1)); + assertTrue(rs.next()); + assertEquals(60, rs.getInt(1)); + assertFalse(rs.next()); } finally { conn.close(); } } @Test - public void testUnionAllInSubqueryDerived() throws Exception { + public void testUnionAllInSubquery() throws Exception { Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES); Connection conn = DriverManager.getConnection(getUrl(), props); conn.setAutoCommit(false); @@ -460,15 +501,43 @@ public class UnionAllIT extends BaseOwnClusterHBaseManagedTimeIT { " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); + String dml = "UPSERT INTO test_table VALUES(?, ?)"; + PreparedStatement stmt = conn.prepareStatement(dml); + stmt.setString(1, "a"); + stmt.setInt(2, 10); + stmt.execute(); + stmt.setString(1, "b"); + stmt.setInt(2, 20); + stmt.execute(); + conn.commit(); + ddl = "CREATE TABLE b_table " + " (a_string varchar not null, col1 integer" + " CONSTRAINT pk PRIMARY KEY (a_string))\n"; createTestTable(getUrl(), ddl); - ddl = "select a_string, col1 from test_table where a_string in (select a_string from " + - "(select * from test_table union all select * from b_table))"; - conn.createStatement().executeQuery(ddl); - } catch (SQLException e) { + dml = "UPSERT INTO b_table VALUES(?, ?)"; + stmt = conn.prepareStatement(dml); + stmt.setString(1, "a"); + stmt.setInt(2, 30); + stmt.execute(); + stmt.setString(1, "c"); + stmt.setInt(2, 60); + stmt.execute(); + conn.commit(); + + String[] queries = new String[2]; + queries[0] = "select a_string, col1 from test_table where a_string in " + + "(select a_string aa from b_table where a_string != 'a' union all select a_string bb from b_table)"; + queries[1] = "select a_string, col1 from test_table where a_string in (select a_string from " + + "(select a_string from b_table where a_string != 'a' union all select a_string from b_table))"; + for (String query : queries) { + ResultSet rs = conn.createStatement().executeQuery(query); + assertTrue(rs.next()); + assertEquals("a", rs.getString(1)); + assertEquals(10, rs.getInt(2)); + assertFalse(rs.next()); + } } finally { conn.close(); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 295bd79..9f60424 100644 --- a/phoenix-core/src/main/antlr3/PhoenixSQL.g +++ b/phoenix-core/src/main/antlr3/PhoenixSQL.g @@ -586,7 +586,7 @@ single_select returns [SelectStatement ret] (WHERE where=expression)? (GROUP BY group=group_by)? (HAVING having=expression)? - { ParseContext context = contextStack.peek(); $ret = factory.select(from, h, d!=null, sel, where, group, having, null, null, getBindCount(), context.isAggregate(), context.hasSequences()); } + { ParseContext context = contextStack.peek(); $ret = factory.select(from, h, d!=null, sel, where, group, having, null, null, getBindCount(), context.isAggregate(), context.hasSequences(), null); } ; finally{ contextStack.pop(); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java index 322d24a..b8e68f9 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/DeleteCompiler.java @@ -322,7 +322,7 @@ public class DeleteCompiler { hint, false, aliasedNodes, delete.getWhere(), Collections.<ParseNode>emptyList(), null, delete.getOrderBy(), delete.getLimit(), - delete.getBindCount(), false, false); + delete.getBindCount(), false, false, Collections.<SelectStatement>emptyList()); select = StatementNormalizer.normalize(select, resolver); SelectStatement transformedSelect = SubqueryRewriter.transform(select, resolver, connection); if (transformedSelect != select) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 98a1108..da78b24 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 @@ -469,7 +469,7 @@ public class FromCompiler { if (node instanceof WildcardParseNode || node instanceof TableWildcardParseNode || node instanceof FamilyWildcardParseNode) - throw new SQLException("Encountered wildcard in subqueries."); + throw new SQLFeatureNotSupportedException("Wildcard in subqueries not supported."); alias = SchemaUtil.normalizeIdentifier(node.getAlias()); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 98b7edb..af6c712 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 @@ -688,7 +688,7 @@ public class JoinCompiler { if (isSubselect()) return SubselectRewriter.applyOrderBy(SubselectRewriter.applyPostFilters(subselect, preFilters, tableNode.getAlias()), orderBy, tableNode.getAlias()); - return NODE_FACTORY.select(tableNode, select.getHint(), false, selectNodes, getPreFiltersCombined(), null, null, orderBy, null, 0, false, select.hasSequence()); + return NODE_FACTORY.select(tableNode, select.getHint(), false, selectNodes, getPreFiltersCombined(), null, null, orderBy, null, 0, false, select.hasSequence(), Collections.<SelectStatement>emptyList()); } public boolean hasFilters() { @@ -1267,7 +1267,7 @@ public class JoinCompiler { String tableAlias = tableRef.getTableAlias(); TableNode from = NODE_FACTORY.namedTable(tableAlias == null ? null : '"' + tableAlias + '"', tName, dynamicCols); - return NODE_FACTORY.select(from, hintNode, false, selectList, where, groupBy, null, orderBy, null, 0, groupBy != null, hasSequence); + return NODE_FACTORY.select(from, hintNode, false, selectList, where, groupBy, null, orderBy, null, 0, groupBy != null, hasSequence, Collections.<SelectStatement>emptyList()); } public static PTable joinProjectedTables(PTable left, PTable right, JoinType type) throws SQLException { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 f8177e6..16a7a33 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 @@ -174,7 +174,7 @@ public class QueryCompiler { } UnionCompiler.checkProjectionNumAndTypes(plans); - TableRef tableRef = UnionCompiler.contructSchemaTable(statement, plans.get(0)); + TableRef tableRef = UnionCompiler.contructSchemaTable(statement, plans.get(0), select.hasWildcard() ? null : select.getSelect()); ColumnResolver resolver = FromCompiler.getResolver(tableRef); StatementContext context = new StatementContext(statement, resolver, scan, sequenceManager); @@ -422,7 +422,7 @@ public class QueryCompiler { context.setResolver(resolver); TableNode from = NODE_FACTORY.namedTable(tableRef.getTableAlias(), NODE_FACTORY.table(tableRef.getTable().getSchemaName().getString(), tableRef.getTable().getTableName().getString())); ParseNode where = joinTable.getPostFiltersCombined(); - SelectStatement select = asSubquery ? NODE_FACTORY.select(from, joinTable.getStatement().getHint(), false, Collections.<AliasedNode> emptyList(), where, null, null, orderBy, null, 0, false, joinTable.getStatement().hasSequence()) + SelectStatement select = asSubquery ? NODE_FACTORY.select(from, joinTable.getStatement().getHint(), false, Collections.<AliasedNode> emptyList(), where, null, null, orderBy, null, 0, false, joinTable.getStatement().hasSequence(), Collections.<SelectStatement>emptyList()) : NODE_FACTORY.select(joinTable.getStatement(), from, where); return compileSingleFlatQuery(context, select, binds, asSubquery, false, innerPlan, null, isInRowKeyOrder); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/phoenix-core/src/main/java/org/apache/phoenix/compile/SubqueryRewriter.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/SubqueryRewriter.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/SubqueryRewriter.java index 60067e5..8e887a8 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/SubqueryRewriter.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/SubqueryRewriter.java @@ -36,6 +36,7 @@ import org.apache.phoenix.parse.ColumnParseNode; import org.apache.phoenix.parse.ComparisonParseNode; import org.apache.phoenix.parse.CompoundParseNode; import org.apache.phoenix.parse.ExistsParseNode; +import org.apache.phoenix.parse.HintNode; import org.apache.phoenix.parse.InParseNode; import org.apache.phoenix.parse.JoinTableNode.JoinType; import org.apache.phoenix.parse.LiteralParseNode; @@ -139,7 +140,7 @@ public class SubqueryRewriter extends ParseNodeRewriter { } SubqueryParseNode subqueryNode = (SubqueryParseNode) l.get(1); - SelectStatement subquery = subqueryNode.getSelectNode(); + SelectStatement subquery = fixSubqueryStatement(subqueryNode.getSelectNode()); String rhsTableAlias = ParseNodeFactory.createTempAlias(); List<AliasedNode> selectNodes = fixAliasedNodes(subquery.getSelect(), true); subquery = NODE_FACTORY.select(subquery, !node.isSubqueryDistinct(), selectNodes); @@ -160,7 +161,7 @@ public class SubqueryRewriter extends ParseNodeRewriter { } SubqueryParseNode subqueryNode = (SubqueryParseNode) l.get(0); - SelectStatement subquery = subqueryNode.getSelectNode(); + SelectStatement subquery = fixSubqueryStatement(subqueryNode.getSelectNode()); String rhsTableAlias = ParseNodeFactory.createTempAlias(); JoinConditionExtractor conditionExtractor = new JoinConditionExtractor(subquery, resolver, connection, rhsTableAlias); ParseNode where = subquery.getWhere() == null ? null : subquery.getWhere().accept(conditionExtractor); @@ -199,7 +200,7 @@ public class SubqueryRewriter extends ParseNodeRewriter { } SubqueryParseNode subqueryNode = (SubqueryParseNode) secondChild; - SelectStatement subquery = subqueryNode.getSelectNode(); + SelectStatement subquery = fixSubqueryStatement(subqueryNode.getSelectNode()); String rhsTableAlias = ParseNodeFactory.createTempAlias(); JoinConditionExtractor conditionExtractor = new JoinConditionExtractor(subquery, resolver, connection, rhsTableAlias); ParseNode where = subquery.getWhere() == null ? null : subquery.getWhere().accept(conditionExtractor); @@ -282,7 +283,7 @@ public class SubqueryRewriter extends ParseNodeRewriter { } SubqueryParseNode subqueryNode = (SubqueryParseNode) firstChild; - SelectStatement subquery = subqueryNode.getSelectNode(); + SelectStatement subquery = fixSubqueryStatement(subqueryNode.getSelectNode()); String rhsTableAlias = ParseNodeFactory.createTempAlias(); JoinConditionExtractor conditionExtractor = new JoinConditionExtractor(subquery, resolver, connection, rhsTableAlias); ParseNode where = subquery.getWhere() == null ? null : subquery.getWhere().accept(conditionExtractor); @@ -339,7 +340,7 @@ public class SubqueryRewriter extends ParseNodeRewriter { groupbyNodes.set(i - 1, aliasedNode.getNode()); } SelectStatement derivedTableStmt = NODE_FACTORY.select(subquery, subquery.isDistinct(), derivedTableSelect, where, derivedTableGroupBy, true); - subquery = NODE_FACTORY.select(NODE_FACTORY.derivedTable(derivedTableAlias, derivedTableStmt), subquery.getHint(), false, selectNodes, null, groupbyNodes, null, Collections.<OrderByNode> emptyList(), null, subquery.getBindCount(), true, false); + subquery = NODE_FACTORY.select(NODE_FACTORY.derivedTable(derivedTableAlias, derivedTableStmt), subquery.getHint(), false, selectNodes, null, groupbyNodes, null, Collections.<OrderByNode> emptyList(), null, subquery.getBindCount(), true, false, Collections.<SelectStatement>emptyList()); } ParseNode onNode = conditionExtractor.getJoinCondition(); @@ -357,6 +358,14 @@ public class SubqueryRewriter extends ParseNodeRewriter { return Lists.newArrayList(firstChild, secondChild); } + private SelectStatement fixSubqueryStatement(SelectStatement select) { + if (!select.isUnion()) + return select; + + // Wrap as a derived table. + return NODE_FACTORY.select(NODE_FACTORY.derivedTable(ParseNodeFactory.createTempAlias(), select), HintNode.EMPTY_HINT_NODE, false, select.getSelect(), null, null, null, null, null, select.getBindCount(), false, false, Collections.<SelectStatement> emptyList()); + } + private List<AliasedNode> fixAliasedNodes(List<AliasedNode> nodes, boolean addSelectOne) { List<AliasedNode> normNodes = Lists.<AliasedNode> newArrayListWithExpectedSize(nodes.size() + (addSelectOne ? 1 : 0)); if (addSelectOne) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java index 3f069ff..269232e 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UnionCompiler.java @@ -26,6 +26,7 @@ import org.apache.phoenix.exception.SQLExceptionCode; import org.apache.phoenix.exception.SQLExceptionInfo; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.jdbc.PhoenixStatement; +import org.apache.phoenix.parse.AliasedNode; import org.apache.phoenix.schema.PColumn; import org.apache.phoenix.schema.PColumnImpl; import org.apache.phoenix.schema.PName; @@ -66,12 +67,13 @@ public class UnionCompiler { return selectPlans; } - public static TableRef contructSchemaTable(PhoenixStatement statement, QueryPlan plan) throws SQLException { + public static TableRef contructSchemaTable(PhoenixStatement statement, QueryPlan plan, List<AliasedNode> selectNodes) throws SQLException { List<PColumn> projectedColumns = new ArrayList<PColumn>(); for (int i=0; i< plan.getProjector().getColumnCount(); i++) { ColumnProjector colProj = plan.getProjector().getColumnProjector(i); Expression sourceExpression = colProj.getExpression(); - PColumnImpl projectedColumn = new PColumnImpl(PNameFactory.newName(colProj.getName()), UNION_FAMILY_NAME, + String name = selectNodes == null ? colProj.getName() : selectNodes.get(i).getAlias(); + PColumnImpl projectedColumn = new PColumnImpl(PNameFactory.newName(name), UNION_FAMILY_NAME, sourceExpression.getDataType(), sourceExpression.getMaxLength(), sourceExpression.getScale(), sourceExpression.isNullable(), i, sourceExpression.getSortOrder(), 500, null, false, sourceExpression.toString()); projectedColumns.add(projectedColumn); http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java ---------------------------------------------------------------------- diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java index 462e1f0..dfb9779 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixStatement.java @@ -898,19 +898,11 @@ public class PhoenixStatement implements Statement, SQLCloseable, org.apache.pho protected static class ExecutableNodeFactory extends ParseNodeFactory { @Override - public ExecutableSelectStatement select(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, - ParseNode where, List<ParseNode> groupBy, ParseNode having, - List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate, boolean hasSequence) { - return this.select(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, - Collections.<SelectStatement>emptyList()); - } - - @Override public ExecutableSelectStatement select(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where, List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate, boolean hasSequence, List<SelectStatement> selects) { return new ExecutableSelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy, - having, orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects); + having, orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects == null ? Collections.<SelectStatement>emptyList() : selects); } @Override http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 382bba5..7b3a63a 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 @@ -290,7 +290,7 @@ public class QueryOptimizer { aliasedNodes.add(FACTORY.aliasedNode(null, indexColNode)); nodes.add(new ColumnParseNode(null, '"' + column.getName().getString() + '"')); } - SelectStatement innerSelect = FACTORY.select(indexSelect.getFrom(), indexSelect.getHint(), false, aliasedNodes, where, null, null, null, indexSelect.getLimit(), indexSelect.getBindCount(), false, indexSelect.hasSequence()); + SelectStatement innerSelect = FACTORY.select(indexSelect.getFrom(), indexSelect.getHint(), false, aliasedNodes, where, null, null, null, indexSelect.getLimit(), indexSelect.getBindCount(), false, indexSelect.hasSequence(), Collections.<SelectStatement>emptyList()); ParseNode outerWhere = FACTORY.in(nodes.size() == 1 ? nodes.get(0) : FACTORY.rowValueConstructor(nodes), FACTORY.subquery(innerSelect, false), false, true); ParseNode extractedCondition = whereRewriter.getExtractedCondition(); if (extractedCondition != null) { http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 0f5074e..5eb641e 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 @@ -20,7 +20,6 @@ package org.apache.phoenix.parse; import java.lang.reflect.Constructor; import java.math.BigDecimal; import java.sql.SQLException; -import java.sql.SQLFeatureNotSupportedException; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -29,6 +28,8 @@ import java.util.concurrent.atomic.AtomicInteger; import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; import org.apache.hadoop.hbase.util.Pair; +import org.apache.phoenix.exception.SQLExceptionCode; +import org.apache.phoenix.exception.SQLExceptionInfo; import org.apache.phoenix.exception.UnknownFunctionException; import org.apache.phoenix.expression.Expression; import org.apache.phoenix.expression.ExpressionType; @@ -659,15 +660,8 @@ public class ParseNodeFactory { boolean hasSequence, List<SelectStatement> selects) { return new SelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy, having, - orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects); + orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence, selects == null ? Collections.<SelectStatement>emptyList() : selects); } - - public SelectStatement select(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, ParseNode where, - List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate, boolean hasSequence) { - - return new SelectStatement(from, hint, isDistinct, select, where, groupBy == null ? Collections.<ParseNode>emptyList() : groupBy, having, - orderBy == null ? Collections.<OrderByNode>emptyList() : orderBy, limit, bindCount, isAggregate, hasSequence); - } public UpsertStatement upsert(NamedTableNode table, HintNode hint, List<ColumnName> columns, List<ParseNode> values, SelectStatement select, int bindCount) { return new UpsertStatement(table, hint, columns, values, select, bindCount); @@ -679,53 +673,53 @@ public class ParseNodeFactory { public SelectStatement select(SelectStatement statement, ParseNode where) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), statement.getHaving(), - statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, ParseNode where, ParseNode having) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), having, - statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, List<AliasedNode> select, ParseNode where, List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), - select, where, groupBy, having, orderBy, statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + select, where, groupBy, having, orderBy, statement.getLimit(), statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, TableNode table) { return select(table, statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, TableNode table, ParseNode where) { return select(table, statement.getHint(), statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, boolean isDistinct, List<AliasedNode> select) { return select(statement.getFrom(), statement.getHint(), isDistinct, select, statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, boolean isDistinct, List<AliasedNode> select, ParseNode where) { return select(statement.getFrom(), statement.getHint(), isDistinct, select, where, statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, boolean isDistinct, List<AliasedNode> select, ParseNode where, List<ParseNode> groupBy, boolean isAggregate) { return select(statement.getFrom(), statement.getHint(), isDistinct, select, where, groupBy, statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), isAggregate, - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, List<OrderByNode> orderBy) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, statement.getLimit(), - statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, HintNode hint) { @@ -737,39 +731,65 @@ public class ParseNodeFactory { public SelectStatement select(SelectStatement statement, HintNode hint, ParseNode where) { return select(statement.getFrom(), hint, statement.isDistinct(), statement.getSelect(), where, statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), statement.getLimit(), statement.getBindCount(), statement.isAggregate(), - statement.hasSequence()); + statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, limit, - bindCount, isAggregate || statement.isAggregate(), statement.hasSequence()); + bindCount, isAggregate || statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, LimitNode limit) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), statement.getOrderBy(), limit, - statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(SelectStatement statement, List<OrderByNode> orderBy, LimitNode limit) { return select(statement.getFrom(), statement.getHint(), statement.isDistinct(), statement.getSelect(), statement.getWhere(), statement.getGroupBy(), statement.getHaving(), orderBy, limit, - statement.getBindCount(), statement.isAggregate(), statement.hasSequence()); + statement.getBindCount(), statement.isAggregate(), statement.hasSequence(), statement.getSelects()); } public SelectStatement select(List<SelectStatement> statements, List<OrderByNode> orderBy, LimitNode limit, int bindCount, boolean isAggregate) { if (statements.size() == 1) - return select(statements.get(0), orderBy, limit, bindCount, isAggregate); + return select(statements.get(0), orderBy, limit, bindCount, isAggregate); + + // Get a list of adjusted aliases from a non-wildcard sub-select if any. + // We do not check the number of select nodes among all sub-selects, as + // it will be done later at compile stage. Empty or different aliases + // are ignored, since they cannot be referred by outer queries. + List<String> aliases = Lists.<String> newArrayList(); + for (int i = 0; i < statements.size() && aliases.isEmpty(); i++) { + SelectStatement subselect = statements.get(i); + if (!subselect.hasWildcard()) { + for (AliasedNode aliasedNode : subselect.getSelect()) { + String alias = aliasedNode.getAlias(); + if (alias == null) { + alias = SchemaUtil.normalizeIdentifier(aliasedNode.getNode().getAlias()); + } + aliases.add(alias == null ? createTempAlias() : alias); + } + } + } + + List<AliasedNode> aliasedNodes; + if (aliases.isEmpty()) { + aliasedNodes = Lists.newArrayList(aliasedNode(null, wildcard())); + } else { + aliasedNodes = Lists.newArrayListWithExpectedSize(aliases.size()); + for (String alias : aliases) { + aliasedNodes.add(aliasedNode(alias, column(null, alias, alias))); + } + } - return select(null, HintNode.EMPTY_HINT_NODE, false, Lists.newArrayList(aliasedNode(null, wildcard())), + return select(null, HintNode.EMPTY_HINT_NODE, false, aliasedNodes, null, null, null, orderBy, limit, bindCount, false, false, statements); } public SubqueryParseNode subquery(SelectStatement select, boolean expectSingleRow) { - if (select.isUnion()) - throw new RuntimeException(new SQLFeatureNotSupportedException()); return new SubqueryParseNode(select, expectSingleRow); } http://git-wip-us.apache.org/repos/asf/phoenix/blob/9ddb484a/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 08cec87..44b24af 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 @@ -42,7 +42,7 @@ public class SelectStatement implements FilterableStatement { Collections.<AliasedNode>singletonList(new AliasedNode(null, LiteralParseNode.ONE)), null, Collections.<ParseNode>emptyList(), null, Collections.<OrderByNode>emptyList(), - null, 0, false, false); + null, 0, false, false, Collections.<SelectStatement>emptyList()); public static final SelectStatement COUNT_ONE = new SelectStatement( null, null, false, @@ -54,14 +54,14 @@ public class SelectStatement implements FilterableStatement { new BuiltInFunctionInfo(CountAggregateFunction.class, CountAggregateFunction.class.getAnnotation(BuiltInFunction.class))))), null, Collections.<ParseNode>emptyList(), null, Collections.<OrderByNode>emptyList(), - null, 0, true, false); + null, 0, true, false, Collections.<SelectStatement>emptyList()); public static SelectStatement create(SelectStatement select, HintNode hint) { if (select.getHint() == hint || hint.isEmpty()) { return select; } return new SelectStatement(select.getFrom(), hint, select.isDistinct(), select.getSelect(), select.getWhere(), select.getGroupBy(), select.getHaving(), - select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate(), select.hasSequence()); + select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate(), select.hasSequence(), select.getSelects()); } public SelectStatement combine(ParseNode where) { @@ -73,13 +73,13 @@ public class SelectStatement implements FilterableStatement { } return new SelectStatement(this.getFrom(), this.getHint(), this.isDistinct(), this.getSelect(), where, this.getGroupBy(), this.getHaving(), - this.getOrderBy(), this.getLimit(), this.getBindCount(), this.isAggregate(), this.hasSequence()); + this.getOrderBy(), this.getLimit(), this.getBindCount(), this.isAggregate(), this.hasSequence(), this.selects); } public static SelectStatement create(SelectStatement select, List<AliasedNode> selects) { return new SelectStatement(select.getFrom(), select.getHint(), select.isDistinct(), selects, select.getWhere(), select.getGroupBy(), select.getHaving(), - select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate(), select.hasSequence()); + select.getOrderBy(), select.getLimit(), select.getBindCount(), select.isAggregate(), select.hasSequence(), select.getSelects()); } // Copy constructor for sub select statements in a union @@ -87,7 +87,7 @@ public class SelectStatement implements FilterableStatement { List<OrderByNode> orderBy, LimitNode limit, boolean isAggregate) { return new SelectStatement(select.getFrom(), select.getHint(), select.isDistinct(), select.getSelect(), select.getWhere(), select.getGroupBy(), select.getHaving(), - orderBy, limit, select.getBindCount(), isAggregate, select.hasSequence()); + orderBy, limit, select.getBindCount(), isAggregate, select.hasSequence(), select.getSelects()); } private final TableNode fromTable; @@ -102,6 +102,7 @@ public class SelectStatement implements FilterableStatement { private final int bindCount; private final boolean isAggregate; private final boolean hasSequence; + private final boolean hasWildcard; private final List<SelectStatement> selects = new ArrayList<SelectStatement>(); @Override @@ -228,17 +229,19 @@ public class SelectStatement implements FilterableStatement { this.bindCount = bindCount; this.isAggregate = isAggregate || groupBy.size() != countConstants(groupBy) || this.having != null; this.hasSequence = hasSequence; + boolean hasWildcard = false; + for (AliasedNode aliasedNode : select) { + ParseNode node = aliasedNode.getNode(); + if (node instanceof WildcardParseNode || node instanceof TableWildcardParseNode || node instanceof FamilyWildcardParseNode) { + hasWildcard = true; + break; + } + } + this.hasWildcard = hasWildcard; if (!selects.isEmpty()) { this.selects.addAll(selects); } } - - public SelectStatement(TableNode from, HintNode hint, boolean isDistinct, List<AliasedNode> select, - ParseNode where, List<ParseNode> groupBy, ParseNode having, List<OrderByNode> orderBy, LimitNode limit, - int bindCount, boolean isAggregate, boolean hasSequence) { - this(from, hint, isDistinct, select, where, groupBy, having, orderBy, limit, bindCount, isAggregate, hasSequence, - Collections.<SelectStatement>emptyList()); - } @Override public boolean isDistinct() { @@ -326,4 +329,8 @@ public class SelectStatement implements FilterableStatement { public List<SelectStatement> getSelects() { return selects; } + + public boolean hasWildcard() { + return hasWildcard; + } }
