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 2015/04/07 05:16:34 UTC

phoenix git commit: PHOENIX-1807 Support UNION queries in subquery

Repository: phoenix
Updated Branches:
  refs/heads/master 7aea69215 -> 9ddb484aa


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/master
Commit: 9ddb484aaf0b84b729be9b699b0528813b4ffb1b
Parents: 7aea692
Author: maryannxue <we...@intel.com>
Authored: Mon Apr 6 23:16:20 2015 -0400
Committer: maryannxue <we...@intel.com>
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;
+    }
 }