Repository: phoenix
Updated Branches:
  refs/heads/master 0440aca51 -> 3f829751d


http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
index 27fe0f9..e84ca2a 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/ProjectionCompiler.java
@@ -62,6 +62,7 @@ import org.apache.phoenix.parse.TableName;
 import org.apache.phoenix.parse.TableWildcardParseNode;
 import org.apache.phoenix.parse.WildcardParseNode;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.AmbiguousColumnException;
 import org.apache.phoenix.schema.ArgumentTypeMismatchException;
 import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
 import org.apache.phoenix.schema.ColumnNotFoundException;
@@ -144,12 +145,21 @@ public class ProjectionCompiler {
             }
             ColumnRef ref = new ColumnRef(tableRef,i);
             String colName = ref.getColumn().getName().getString();
+            String tableAlias = tableRef.getTableAlias();
             if (resolveColumn) {
-                if (tableRef.getTableAlias() != null) {
-                    ref = resolver.resolveColumn(null, 
tableRef.getTableAlias(), colName);
-                } else {
-                    String schemaName = table.getSchemaName().getString();
-                    ref = resolver.resolveColumn(schemaName.length() == 0 ? 
null : schemaName, table.getTableName().getString(), colName);
+                try {
+                    if (tableAlias != null) {
+                        ref = resolver.resolveColumn(null, tableAlias, 
colName);
+                    } else {
+                        String schemaName = table.getSchemaName().getString();
+                        ref = resolver.resolveColumn(schemaName.length() == 0 
? null : schemaName, table.getTableName().getString(), colName);
+                    }
+                } catch (AmbiguousColumnException e) {
+                    if (column.getFamilyName() != null) {
+                        ref = resolver.resolveColumn(tableAlias != null ? 
tableAlias : table.getTableName().getString(), 
column.getFamilyName().getString(), colName);
+                    } else {
+                        throw e;
+                    }
                 }
             }
             Expression expression = ref.newColumnExpression();
@@ -219,12 +229,21 @@ public class ProjectionCompiler {
                 }
             }
             String colName = tableColumn.getName().getString();
+            String tableAlias = tableRef.getTableAlias();
             if (resolveColumn) {
-                if (tableRef.getTableAlias() != null) {
-                    ref = resolver.resolveColumn(null, 
tableRef.getTableAlias(), indexColName);
-                } else {
-                    String schemaName = index.getSchemaName().getString();
-                    ref = resolver.resolveColumn(schemaName.length() == 0 ? 
null : schemaName, index.getTableName().getString(), indexColName);
+                try {
+                    if (tableAlias != null) {
+                        ref = resolver.resolveColumn(null, tableAlias, 
indexColName);
+                    } else {
+                        String schemaName = index.getSchemaName().getString();
+                        ref = resolver.resolveColumn(schemaName.length() == 0 
? null : schemaName, index.getTableName().getString(), indexColName);
+                    }
+                } catch (AmbiguousColumnException e) {
+                    if (indexColumn.getFamilyName() != null) {
+                        ref = resolver.resolveColumn(tableAlias != null ? 
tableAlias : index.getTableName().getString(), 
indexColumn.getFamilyName().getString(), indexColName);
+                    } else {
+                        throw e;
+                    }
                 }
             }
             Expression expression = ref.newColumnExpression();
@@ -238,11 +257,14 @@ public class ProjectionCompiler {
         }
     }
     
-    private static void projectTableColumnFamily(StatementContext context, 
String cfName, TableRef tableRef, List<Expression> projectedExpressions, 
List<ExpressionProjector> projectedColumns) throws SQLException {
+    private static void projectTableColumnFamily(StatementContext context, 
String cfName, TableRef tableRef, boolean resolveColumn, List<Expression> 
projectedExpressions, List<ExpressionProjector> projectedColumns) throws 
SQLException {
         PTable table = tableRef.getTable();
         PColumnFamily pfamily = table.getColumnFamily(cfName);
         for (PColumn column : pfamily.getColumns()) {
             ColumnRef ref = new ColumnRef(tableRef, column.getPosition());
+            if (resolveColumn) {
+                ref = 
context.getResolver().resolveColumn(table.getTableName().getString(), cfName, 
column.getName().getString());
+            }
             Expression expression = ref.newColumnExpression();
             projectedExpressions.add(expression);
             String colName = column.getName().toString();
@@ -252,7 +274,7 @@ public class ProjectionCompiler {
         }
     }
 
-    private static void projectIndexColumnFamily(StatementContext context, 
String cfName, TableRef tableRef, List<Expression> projectedExpressions, 
List<ExpressionProjector> projectedColumns) throws SQLException {
+    private static void projectIndexColumnFamily(StatementContext context, 
String cfName, TableRef tableRef, boolean resolveColumn, List<Expression> 
projectedExpressions, List<ExpressionProjector> projectedColumns) throws 
SQLException {
         PTable index = tableRef.getTable();
         PhoenixConnection conn = context.getConnection();
         String tableName = index.getParentName().getString();
@@ -277,6 +299,9 @@ public class ProjectionCompiler {
                     throw e;
                 }
             }
+            if (resolveColumn) {
+                ref = 
context.getResolver().resolveColumn(index.getTableName().getString(), 
indexColumn.getFamilyName() == null ? null : 
indexColumn.getFamilyName().getString(), indexColName);
+            }
             Expression expression = ref.newColumnExpression();
             projectedExpressions.add(expression);
             String colName = column.getName().toString();
@@ -322,6 +347,7 @@ public class ProjectionCompiler {
         ColumnResolver resolver = context.getResolver();
         TableRef tableRef = context.getCurrentTable();
         PTable table = tableRef.getTable();
+        boolean resolveColumn = !tableRef.equals(resolver.getTables().get(0));
         boolean isWildcard = false;
         Scan scan = context.getScan();
         int index = 0;
@@ -336,9 +362,9 @@ public class ProjectionCompiler {
                 }
                 isWildcard = true;
                 if (tableRef.getTable().getType() == PTableType.INDEX && 
((WildcardParseNode)node).isRewrite()) {
-                       projectAllIndexColumns(context, tableRef, false, 
projectedExpressions, projectedColumns, targetColumns);
+                       projectAllIndexColumns(context, tableRef, 
resolveColumn, projectedExpressions, projectedColumns, targetColumns);
                 } else {
-                    projectAllTableColumns(context, tableRef, false, 
projectedExpressions, projectedColumns, targetColumns);
+                    projectAllTableColumns(context, tableRef, resolveColumn, 
projectedExpressions, projectedColumns, targetColumns);
                 }
             } else if (node instanceof TableWildcardParseNode) {
                 TableName tName = ((TableWildcardParseNode) 
node).getTableName();
@@ -362,9 +388,9 @@ public class ProjectionCompiler {
                 // columns are projected (which is currently true, but could 
change).
                 projectedFamilies.add(Bytes.toBytes(cfName));
                 if (tableRef.getTable().getType() == PTableType.INDEX && 
((FamilyWildcardParseNode)node).isRewrite()) {
-                    projectIndexColumnFamily(context, cfName, tableRef, 
projectedExpressions, projectedColumns);
+                    projectIndexColumnFamily(context, cfName, tableRef, 
resolveColumn, projectedExpressions, projectedColumns);
                 } else {
-                    projectTableColumnFamily(context, cfName, tableRef, 
projectedExpressions, projectedColumns);
+                    projectTableColumnFamily(context, cfName, tableRef, 
resolveColumn, projectedExpressions, projectedColumns);
                 }
             } else {
                 Expression expression = node.accept(selectVisitor);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/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 137f4e9..2276f4e 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
@@ -28,9 +28,6 @@ import org.apache.hadoop.hbase.util.Pair;
 import org.apache.phoenix.compile.GroupByCompiler.GroupBy;
 import org.apache.phoenix.compile.JoinCompiler.JoinSpec;
 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;
@@ -100,14 +97,23 @@ public class QueryCompiler {
     private final List<? extends PDatum> targetColumns;
     private final ParallelIteratorFactory parallelIteratorFactory;
     private final SequenceManager sequenceManager;
+    private final boolean projectTuples;
     private final boolean useSortMergeJoin;
     private final boolean noChildParentJoinOptimization;
 
     public QueryCompiler(PhoenixStatement statement, SelectStatement select, 
ColumnResolver resolver) throws SQLException {
-        this(statement, select, resolver, Collections.<PDatum>emptyList(), 
null, new SequenceManager(statement));
+        this(statement, select, resolver, Collections.<PDatum>emptyList(), 
null, new SequenceManager(statement), true);
+    }
+
+    public QueryCompiler(PhoenixStatement statement, SelectStatement select, 
ColumnResolver resolver, boolean projectTuples) throws SQLException {
+        this(statement, select, resolver, Collections.<PDatum>emptyList(), 
null, new SequenceManager(statement), projectTuples);
     }
 
     public QueryCompiler(PhoenixStatement statement, SelectStatement select, 
ColumnResolver resolver, List<? extends PDatum> targetColumns, 
ParallelIteratorFactory parallelIteratorFactory, SequenceManager 
sequenceManager) throws SQLException {
+        this(statement, select, resolver, targetColumns, 
parallelIteratorFactory, sequenceManager, true);
+    }
+    
+    public QueryCompiler(PhoenixStatement statement, SelectStatement select, 
ColumnResolver resolver, List<? extends PDatum> targetColumns, 
ParallelIteratorFactory parallelIteratorFactory, SequenceManager 
sequenceManager, boolean projectTuples) throws SQLException {
         this.statement = statement;
         this.select = select;
         this.resolver = resolver;
@@ -115,6 +121,7 @@ public class QueryCompiler {
         this.targetColumns = targetColumns;
         this.parallelIteratorFactory = parallelIteratorFactory;
         this.sequenceManager = sequenceManager;
+        this.projectTuples = projectTuples;
         this.useSortMergeJoin = 
select.getHint().hasHint(Hint.USE_SORT_MERGE_JOIN);
         this.noChildParentJoinOptimization = 
select.getHint().hasHint(Hint.NO_CHILD_PARENT_JOIN_OPTIMIZATION);
         if 
(statement.getConnection().getQueryServices().getLowestClusterHBaseVersion() >= 
PhoenixDatabaseMetaData.ESSENTIAL_FAMILY_VERSION_THRESHOLD) {
@@ -194,30 +201,32 @@ public class QueryCompiler {
             SelectStatement subquery = table.getAsSubquery(orderBy);
             if (!table.isSubselect()) {
                 context.setCurrentTable(table.getTableRef());
-                ProjectedPTableWrapper projectedTable = 
table.createProjectedTable(!projectPKColumns, context);
-                TupleProjector.serializeProjectorIntoScan(context.getScan(), 
projectedTable.createTupleProjector());
-                context.setResolver(projectedTable.createColumnResolver());
+                PTable projectedTable = 
table.createProjectedTable(!projectPKColumns, context);
+                TupleProjector.serializeProjectorIntoScan(context.getScan(), 
new TupleProjector(projectedTable));
+                
context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable));
                 table.projectColumns(context.getScan());
                 return compileSingleQuery(context, subquery, binds, 
asSubquery, !asSubquery);
             }
             QueryPlan plan = compileSubquery(subquery);
-            ProjectedPTableWrapper projectedTable = 
table.createProjectedTable(plan.getProjector());
-            context.setResolver(projectedTable.createColumnResolver());
-            return new TupleProjectionPlan(plan, 
projectedTable.createTupleProjector(), 
table.compilePostFilterExpression(context));
+            PTable projectedTable = 
table.createProjectedTable(plan.getProjector());
+            
context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable));
+            return new TupleProjectionPlan(plan, new 
TupleProjector(plan.getProjector()), 
table.compilePostFilterExpression(context));
         }
 
         boolean[] starJoinVector;
         if (!this.useSortMergeJoin && (starJoinVector = 
joinTable.getStarJoinVector()) != null) {
             Table table = joinTable.getTable();
-            ProjectedPTableWrapper initialProjectedTable;
+            PTable initialProjectedTable;
             TableRef tableRef;
             SelectStatement query;
+            TupleProjector tupleProjector;
             if (!table.isSubselect()) {
                 context.setCurrentTable(table.getTableRef());
                 initialProjectedTable = 
table.createProjectedTable(!projectPKColumns, context);
                 tableRef = table.getTableRef();
                 table.projectColumns(context.getScan());
                 query = 
joinTable.getAsSingleSubquery(table.getAsSubquery(orderBy), asSubquery);
+                tupleProjector = new TupleProjector(initialProjectedTable);
             } else {
                 SelectStatement subquery = table.getAsSubquery(orderBy);
                 QueryPlan plan = compileSubquery(subquery);
@@ -225,9 +234,10 @@ public class QueryCompiler {
                 tableRef = plan.getTableRef();
                 
context.getScan().setFamilyMap(plan.getContext().getScan().getFamilyMap());
                 query = joinTable.getAsSingleSubquery((SelectStatement) 
plan.getStatement(), asSubquery);
+                tupleProjector = new TupleProjector(plan.getProjector());
             }
             context.setCurrentTable(tableRef);
-            PTableWrapper projectedTable = initialProjectedTable;
+            PTable projectedTable = initialProjectedTable;
             int count = joinSpecs.size();
             ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[count];
             List<Expression>[] joinExpressions = new List[count];
@@ -235,9 +245,7 @@ public class QueryCompiler {
             PTable[] tables = new PTable[count];
             int[] fieldPositions = new int[count];
             HashSubPlan[] subPlans = new HashSubPlan[count];
-            fieldPositions[0] = projectedTable.getTable().getColumns().size() 
- projectedTable.getTable().getPKColumns().size();
-            boolean forceProjection = table.isSubselect();
-            boolean needsProject = forceProjection || asSubquery;
+            fieldPositions[0] = projectedTable.getColumns().size() - 
projectedTable.getPKColumns().size();
             for (int i = 0; i < count; i++) {
                 JoinSpec joinSpec = joinSpecs.get(i);
                 Scan subScan = ScanUtil.newScan(originalScan);
@@ -245,17 +253,12 @@ public class QueryCompiler {
                 QueryPlan joinPlan = compileJoinQuery(subContext, binds, 
joinSpec.getJoinTable(), true, true, null);
                 boolean hasPostReference = 
joinSpec.getJoinTable().hasPostReference();
                 if (hasPostReference) {
-                    PTableWrapper subProjTable = ((JoinedTableColumnResolver) 
subContext.getResolver()).getPTableWrapper();
-                    tables[i] = subProjTable.getTable();
-                    projectedTable = 
projectedTable.mergeProjectedTables(subProjTable, joinSpec.getType());
-                    needsProject = true;
+                    tables[i] = 
subContext.getResolver().getTables().get(0).getTable();
+                    projectedTable = 
JoinCompiler.joinProjectedTables(projectedTable, tables[i], joinSpec.getType());
                 } else {
                     tables[i] = null;
                 }
-                if (!starJoinVector[i]) {
-                    needsProject = true;
-                }
-                context.setResolver((!forceProjection && starJoinVector[i]) ? 
joinTable.getOriginalResolver() : projectedTable.createColumnResolver());
+                
context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable));
                 joinIds[i] = new ImmutableBytesPtr(emptyByteArray); // 
place-holder
                 Pair<List<Expression>, List<Expression>> joinConditions = 
joinSpec.compileJoinConditions(context, subContext, true);
                 joinExpressions[i] = joinConditions.getFirst();
@@ -270,17 +273,14 @@ public class QueryCompiler {
                 }
                 subPlans[i] = new HashSubPlan(i, joinPlan, optimized ? null : 
hashExpressions, joinSpec.isSingleValueOnly(), keyRangeLhsExpression, 
keyRangeRhsExpression);
             }
-            if (needsProject) {
-                TupleProjector.serializeProjectorIntoScan(context.getScan(), 
initialProjectedTable.createTupleProjector());
-            }
-            context.setResolver(needsProject ? 
projectedTable.createColumnResolver() : joinTable.getOriginalResolver());
+            TupleProjector.serializeProjectorIntoScan(context.getScan(), 
tupleProjector);
             QueryPlan plan = compileSingleQuery(context, query, binds, 
asSubquery, !asSubquery && joinTable.isAllLeftJoin());
             Expression postJoinFilterExpression = 
joinTable.compilePostFilterExpression(context, table);
             Integer limit = null;
             if (!query.isAggregate() && !query.isDistinct() && 
query.getOrderBy().isEmpty()) {
                 limit = plan.getLimit();
             }
-            HashJoinInfo joinInfo = new 
HashJoinInfo(projectedTable.getTable(), joinIds, joinExpressions, joinTypes, 
starJoinVector, tables, fieldPositions, postJoinFilterExpression, limit, 
forceProjection);
+            HashJoinInfo joinInfo = new HashJoinInfo(projectedTable, joinIds, 
joinExpressions, joinTypes, starJoinVector, tables, fieldPositions, 
postJoinFilterExpression, limit);
             return HashJoinPlan.create(joinTable.getStatement(), plan, 
joinInfo, subPlans);
         }
 
@@ -296,16 +296,17 @@ public class QueryCompiler {
             Scan subScan = ScanUtil.newScan(originalScan);
             StatementContext lhsCtx = new StatementContext(statement, 
context.getResolver(), subScan, new SequenceManager(statement));
             QueryPlan lhsPlan = compileJoinQuery(lhsCtx, binds, lhsJoin, true, 
true, null);
-            PTableWrapper lhsProjTable = ((JoinedTableColumnResolver) 
lhsCtx.getResolver()).getPTableWrapper();
-            ProjectedPTableWrapper rhsProjTable;
+            PTable rhsProjTable;
             TableRef rhsTableRef;
             SelectStatement rhs;
+            TupleProjector tupleProjector;
             if (!rhsTable.isSubselect()) {
                 context.setCurrentTable(rhsTable.getTableRef());
                 rhsProjTable = 
rhsTable.createProjectedTable(!projectPKColumns, context);
                 rhsTableRef = rhsTable.getTableRef();
                 rhsTable.projectColumns(context.getScan());
                 rhs = 
rhsJoinTable.getAsSingleSubquery(rhsTable.getAsSubquery(orderBy), asSubquery);
+                tupleProjector = new TupleProjector(rhsProjTable);
             } else {
                 SelectStatement subquery = rhsTable.getAsSubquery(orderBy);
                 QueryPlan plan = compileSubquery(subquery);
@@ -313,30 +314,27 @@ public class QueryCompiler {
                 rhsTableRef = plan.getTableRef();
                 
context.getScan().setFamilyMap(plan.getContext().getScan().getFamilyMap());
                 rhs = rhsJoinTable.getAsSingleSubquery((SelectStatement) 
plan.getStatement(), asSubquery);
+                tupleProjector = new TupleProjector(plan.getProjector());
             }
             context.setCurrentTable(rhsTableRef);
-            boolean forceProjection = rhsTable.isSubselect();
-            context.setResolver(forceProjection ? 
rhsProjTable.createColumnResolver() : joinTable.getOriginalResolver());
+            
context.setResolver(FromCompiler.getResolverForProjectedTable(rhsProjTable));
             ImmutableBytesPtr[] joinIds = new ImmutableBytesPtr[] {new 
ImmutableBytesPtr(emptyByteArray)};
             Pair<List<Expression>, List<Expression>> joinConditions = 
lastJoinSpec.compileJoinConditions(lhsCtx, context, true);
             List<Expression> joinExpressions = joinConditions.getSecond();
             List<Expression> hashExpressions = joinConditions.getFirst();
             boolean needsMerge = lhsJoin.hasPostReference();
-            boolean needsProject = forceProjection || asSubquery || needsMerge;
-            PTable lhsTable = needsMerge ? lhsProjTable.getTable() : null;
-            int fieldPosition = needsMerge ? 
rhsProjTable.getTable().getColumns().size() - 
rhsProjTable.getTable().getPKColumns().size() : 0;
-            PTableWrapper projectedTable = needsMerge ? 
rhsProjTable.mergeProjectedTables(lhsProjTable, type == JoinType.Right ? 
JoinType.Left : type) : rhsProjTable;
-            if (needsProject) {
-                TupleProjector.serializeProjectorIntoScan(context.getScan(), 
rhsProjTable.createTupleProjector());
-            }
-            context.setResolver(needsProject ? 
projectedTable.createColumnResolver() : joinTable.getOriginalResolver());
+            PTable lhsTable = needsMerge ? 
lhsCtx.getResolver().getTables().get(0).getTable() : null;
+            int fieldPosition = needsMerge ? rhsProjTable.getColumns().size() 
- rhsProjTable.getPKColumns().size() : 0;
+            PTable projectedTable = needsMerge ? 
JoinCompiler.joinProjectedTables(rhsProjTable, lhsTable, type == JoinType.Right 
? JoinType.Left : type) : rhsProjTable;
+            TupleProjector.serializeProjectorIntoScan(context.getScan(), 
tupleProjector);
+            
context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable));
             QueryPlan rhsPlan = compileSingleQuery(context, rhs, binds, 
asSubquery, !asSubquery && type == JoinType.Right);
             Expression postJoinFilterExpression = 
joinTable.compilePostFilterExpression(context, rhsTable);
             Integer limit = null;
             if (!rhs.isAggregate() && !rhs.isDistinct() && 
rhs.getOrderBy().isEmpty()) {
                 limit = rhsPlan.getLimit();
             }
-            HashJoinInfo joinInfo = new 
HashJoinInfo(projectedTable.getTable(), joinIds, new List[] {joinExpressions}, 
new JoinType[] {type == JoinType.Right ? JoinType.Left : type}, new boolean[] 
{true}, new PTable[] {lhsTable}, new int[] {fieldPosition}, 
postJoinFilterExpression, limit, forceProjection);
+            HashJoinInfo joinInfo = new HashJoinInfo(projectedTable, joinIds, 
new List[] {joinExpressions}, new JoinType[] {type == JoinType.Right ? 
JoinType.Left : type}, new boolean[] {true}, new PTable[] {lhsTable}, new int[] 
{fieldPosition}, postJoinFilterExpression, limit);
             Pair<Expression, Expression> keyRangeExpressions = new 
Pair<Expression, Expression>(null, null);
             getKeyExpressionCombinations(keyRangeExpressions, context, 
joinTable.getStatement(), rhsTableRef, type, joinExpressions, hashExpressions);
             return HashJoinPlan.create(joinTable.getStatement(), rhsPlan, 
joinInfo, new HashSubPlan[] {new HashSubPlan(0, lhsPlan, hashExpressions, 
false, keyRangeExpressions.getFirst(), keyRangeExpressions.getSecond())});
@@ -362,28 +360,27 @@ public class QueryCompiler {
         StatementContext lhsCtx = new StatementContext(statement, 
context.getResolver(), lhsScan, new SequenceManager(statement));
         boolean preserveRowkey = !projectPKColumns && type != JoinType.Full;
         QueryPlan lhsPlan = compileJoinQuery(lhsCtx, binds, lhsJoin, true, 
!preserveRowkey, lhsOrderBy);
-        PTableWrapper lhsProjTable = ((JoinedTableColumnResolver) 
lhsCtx.getResolver()).getPTableWrapper();
+        PTable lhsProjTable = 
lhsCtx.getResolver().getTables().get(0).getTable();
         boolean isInRowKeyOrder = preserveRowkey && 
lhsPlan.getOrderBy().getOrderByExpressions().isEmpty();
         
         Scan rhsScan = ScanUtil.newScan(originalScan);
         StatementContext rhsCtx = new StatementContext(statement, 
context.getResolver(), rhsScan, new SequenceManager(statement));
         QueryPlan rhsPlan = compileJoinQuery(rhsCtx, binds, rhsJoin, true, 
true, rhsOrderBy);
-        PTableWrapper rhsProjTable = ((JoinedTableColumnResolver) 
rhsCtx.getResolver()).getPTableWrapper();
+        PTable rhsProjTable = 
rhsCtx.getResolver().getTables().get(0).getTable();
         
         Pair<List<Expression>, List<Expression>> joinConditions = 
lastJoinSpec.compileJoinConditions(type == JoinType.Right ? rhsCtx : lhsCtx, 
type == JoinType.Right ? lhsCtx : rhsCtx, false);
         List<Expression> lhsKeyExpressions = type == JoinType.Right ? 
joinConditions.getSecond() : joinConditions.getFirst();
         List<Expression> rhsKeyExpressions = type == JoinType.Right ? 
joinConditions.getFirst() : joinConditions.getSecond();
         
         boolean needsMerge = rhsJoin.hasPostReference();
-        PTable rhsTable = needsMerge ? rhsProjTable.getTable() : null;
-        int fieldPosition = needsMerge ? 
lhsProjTable.getTable().getColumns().size() - 
lhsProjTable.getTable().getPKColumns().size() : 0;
-        PTableWrapper projectedTable = needsMerge ? 
lhsProjTable.mergeProjectedTables(rhsProjTable, type == JoinType.Right ? 
JoinType.Left : type) : lhsProjTable;
+        int fieldPosition = needsMerge ? lhsProjTable.getColumns().size() - 
lhsProjTable.getPKColumns().size() : 0;
+        PTable projectedTable = needsMerge ? 
JoinCompiler.joinProjectedTables(lhsProjTable, rhsProjTable, type == 
JoinType.Right ? JoinType.Left : type) : lhsProjTable;
 
-        ColumnResolver resolver = projectedTable.createColumnResolver();
-        TableRef tableRef = ((JoinedTableColumnResolver) 
resolver).getTableRef();
+        ColumnResolver resolver = 
FromCompiler.getResolverForProjectedTable(projectedTable);
+        TableRef tableRef = resolver.getTables().get(0);
         StatementContext subCtx = new StatementContext(statement, resolver, 
ScanUtil.newScan(originalScan), new SequenceManager(statement));
         subCtx.setCurrentTable(tableRef);
-        QueryPlan innerPlan = new SortMergeJoinPlan(subCtx, 
joinTable.getStatement(), tableRef, type == JoinType.Right ? JoinType.Left : 
type, lhsPlan, rhsPlan, lhsKeyExpressions, rhsKeyExpressions, 
projectedTable.getTable(), lhsProjTable.getTable(), rhsTable, fieldPosition, 
lastJoinSpec.isSingleValueOnly());
+        QueryPlan innerPlan = new SortMergeJoinPlan(subCtx, 
joinTable.getStatement(), tableRef, type == JoinType.Right ? JoinType.Left : 
type, lhsPlan, rhsPlan, lhsKeyExpressions, rhsKeyExpressions, projectedTable, 
lhsProjTable, needsMerge ? rhsProjTable : null, fieldPosition, 
lastJoinSpec.isSingleValueOnly());
         context.setCurrentTable(tableRef);
         context.setResolver(resolver);
         TableNode from = NODE_FACTORY.namedTable(tableRef.getTableAlias(), 
NODE_FACTORY.table(tableRef.getTable().getSchemaName().getString(), 
tableRef.getTable().getTableName().getString()));
@@ -440,7 +437,7 @@ public class QueryCompiler {
         }
         int maxRows = this.statement.getMaxRows();
         this.statement.setMaxRows(0); // overwrite maxRows to avoid its impact 
on inner queries.
-        QueryPlan plan = new QueryCompiler(this.statement, subquery, 
resolver).compile();
+        QueryPlan plan = new QueryCompiler(this.statement, subquery, resolver, 
false).compile();
         plan = 
statement.getConnection().getQueryServices().getOptimizer().optimize(statement, 
plan);
         this.statement.setMaxRows(maxRows); // restore maxRows.
         return plan;
@@ -467,7 +464,14 @@ public class QueryCompiler {
     }
 
     protected QueryPlan compileSingleFlatQuery(StatementContext context, 
SelectStatement select, List<Object> binds, boolean asSubquery, boolean 
allowPageFilter, QueryPlan innerPlan, TupleProjector innerPlanTupleProjector, 
boolean isInRowKeyOrder) throws SQLException{
-        PhoenixConnection connection = statement.getConnection();
+        PTable projectedTable = null;
+        if (this.projectTuples) {
+            projectedTable = 
TupleProjectionCompiler.createProjectedTable(select, context);
+            if (projectedTable != null) {
+                
context.setResolver(FromCompiler.getResolverForProjectedTable(projectedTable));
+            }
+        }
+        
         ColumnResolver resolver = context.getResolver();
         TableRef tableRef = context.getCurrentTable();
         PTable table = tableRef.getTable();
@@ -485,15 +489,14 @@ public class QueryCompiler {
         Expression having = HavingCompiler.compile(context, select, groupBy);
         // Don't pass groupBy when building where clause expression, because 
we do not want to wrap these
         // expressions as group by key expressions since they're pre, not post 
filtered.
-        if (innerPlan == null) {
-            context.setResolver(FromCompiler.getResolverForQuery(select, 
connection));
+        if (innerPlan == null && 
!tableRef.equals(resolver.getTables().get(0))) {
+            context.setResolver(FromCompiler.getResolverForQuery(select, 
this.statement.getConnection()));
         }
         Set<SubqueryParseNode> subqueries = Sets.<SubqueryParseNode> 
newHashSet();
         Expression where = WhereCompiler.compile(context, select, viewWhere, 
subqueries);
         context.setResolver(resolver); // recover resolver
         OrderBy orderBy = OrderByCompiler.compile(context, select, groupBy, 
limit, isInRowKeyOrder); 
         RowProjector projector = ProjectionCompiler.compile(context, select, 
groupBy, asSubquery ? Collections.<PDatum>emptyList() : targetColumns);
-
         // Final step is to build the query plan
         if (!asSubquery) {
             int maxRows = statement.getMaxRows();
@@ -506,6 +509,10 @@ public class QueryCompiler {
             }
         }
 
+        if (projectedTable != null) {
+            TupleProjector.serializeProjectorIntoScan(context.getScan(), new 
TupleProjector(projectedTable));
+        }
+        
         QueryPlan plan = innerPlan;
         if (plan == null) {
             ParallelIteratorFactory parallelIteratorFactory = asSubquery ? 
null : this.parallelIteratorFactory;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
new file mode 100644
index 0000000..72e2a26
--- /dev/null
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/TupleProjectionCompiler.java
@@ -0,0 +1,214 @@
+/*
+ * 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.compile;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.phoenix.execute.TupleProjector;
+import org.apache.phoenix.parse.AliasedNode;
+import org.apache.phoenix.parse.ColumnParseNode;
+import org.apache.phoenix.parse.FamilyWildcardParseNode;
+import org.apache.phoenix.parse.OrderByNode;
+import org.apache.phoenix.parse.ParseNode;
+import org.apache.phoenix.parse.ParseNodeFactory;
+import org.apache.phoenix.parse.SelectStatement;
+import org.apache.phoenix.parse.StatelessTraverseAllParseNodeVisitor;
+import org.apache.phoenix.parse.TableName;
+import org.apache.phoenix.parse.WildcardParseNode;
+import org.apache.phoenix.schema.ColumnFamilyNotFoundException;
+import org.apache.phoenix.schema.ColumnNotFoundException;
+import org.apache.phoenix.schema.ColumnRef;
+import org.apache.phoenix.schema.LocalIndexDataColumnRef;
+import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameFactory;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableImpl;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.ProjectedColumn;
+import org.apache.phoenix.schema.TableRef;
+import org.apache.phoenix.schema.PTable.IndexType;
+import org.apache.phoenix.util.IndexUtil;
+import org.apache.phoenix.util.SchemaUtil;
+
+import com.google.common.base.Preconditions;
+
+public class TupleProjectionCompiler {
+    public static final PName PROJECTED_TABLE_SCHEMA = 
PNameFactory.newName(".");
+    private static final ParseNodeFactory NODE_FACTORY = new 
ParseNodeFactory();
+    
+    public static PTable createProjectedTable(SelectStatement select, 
StatementContext context) throws SQLException {
+        Preconditions.checkArgument(!select.isJoin());
+        // Non-group-by or group-by aggregations will create its own projected 
result.
+        if (select.getInnerSelectStatement() != null 
+                || select.isAggregate() 
+                || select.isDistinct()
+                || 
(context.getResolver().getTables().get(0).getTable().getType() != 
PTableType.TABLE
+                && 
context.getResolver().getTables().get(0).getTable().getType() != 
PTableType.INDEX && 
context.getResolver().getTables().get(0).getTable().getType() != 
PTableType.VIEW))
+            return null;
+        
+        List<PColumn> projectedColumns = new ArrayList<PColumn>();
+        boolean isWildcard = false;
+        Set<String> families = new HashSet<String>();
+        ColumnRefVisitor visitor = new ColumnRefVisitor(context);
+        TableRef tableRef = context.getCurrentTable();
+        PTable table = tableRef.getTable();
+
+        for (AliasedNode aliasedNode : select.getSelect()) {
+            ParseNode node = aliasedNode.getNode();
+            if (node instanceof WildcardParseNode) {
+                if (((WildcardParseNode) node).isRewrite()) {
+                    TableRef parentTableRef = FromCompiler.getResolver(
+                            NODE_FACTORY.namedTable(null, 
TableName.create(table.getSchemaName().getString(), 
+                                    table.getParentTableName().getString())), 
context.getConnection()).resolveTable(
+                            table.getSchemaName().getString(),
+                            table.getParentTableName().getString());
+                    for (PColumn column : 
parentTableRef.getTable().getColumns()) {
+                        NODE_FACTORY.column(null, '"' + 
IndexUtil.getIndexColumnName(column) + '"', null).accept(visitor);
+                    }
+                }
+                isWildcard = true;
+            } else if (node instanceof FamilyWildcardParseNode) {
+                FamilyWildcardParseNode familyWildcardNode = 
(FamilyWildcardParseNode) node;
+                String familyName = familyWildcardNode.getName();
+                if (familyWildcardNode.isRewrite()) {
+                    TableRef parentTableRef = FromCompiler.getResolver(
+                            NODE_FACTORY.namedTable(null, 
TableName.create(table.getSchemaName().getString(), 
+                                    table.getParentTableName().getString())), 
context.getConnection()).resolveTable(
+                            table.getSchemaName().getString(),
+                            table.getParentTableName().getString());
+                    for (PColumn column : 
parentTableRef.getTable().getColumnFamily(familyName).getColumns()) {
+                        NODE_FACTORY.column(null, '"' + 
IndexUtil.getIndexColumnName(column) + '"', null).accept(visitor);
+                    }
+                }
+                families.add(familyName);
+            } else {
+                node.accept(visitor);
+            }
+        }
+        if (!isWildcard) {
+            for (OrderByNode orderBy : select.getOrderBy()) {
+                orderBy.getNode().accept(visitor);
+            }
+        }
+
+        boolean hasSaltingColumn = table.getBucketNum() != null;
+        int position = hasSaltingColumn ? 1 : 0;
+        // Always project PK columns first in case there are some PK columns 
added by alter table.
+        for (int i = position; i < table.getPKColumns().size(); i++) {
+            PColumn sourceColumn = table.getPKColumns().get(i);
+            ColumnRef sourceColumnRef = new ColumnRef(tableRef, 
sourceColumn.getPosition());
+            PColumn column = new ProjectedColumn(sourceColumn.getName(), 
sourceColumn.getFamilyName(), 
+                    position++, sourceColumn.isNullable(), sourceColumnRef);
+            projectedColumns.add(column);
+        }
+        for (PColumn sourceColumn : table.getColumns()) {
+            if (SchemaUtil.isPKColumn(sourceColumn))
+                continue;
+            ColumnRef sourceColumnRef = new ColumnRef(tableRef, 
sourceColumn.getPosition());
+            if (!isWildcard 
+                    && !visitor.columnRefSet.contains(sourceColumnRef)
+                    && 
!families.contains(sourceColumn.getFamilyName().getString()))
+                continue;
+            PColumn column = new ProjectedColumn(sourceColumn.getName(), 
sourceColumn.getFamilyName(), 
+                    position++, sourceColumn.isNullable(), sourceColumnRef);
+            projectedColumns.add(column);
+            // Wildcard or FamilyWildcard will be handled by 
ProjectionCompiler.
+            if (!isWildcard && 
!families.contains(sourceColumn.getFamilyName())) {
+                
context.getScan().addColumn(sourceColumn.getFamilyName().getBytes(), 
sourceColumn.getName().getBytes());
+            }
+        }
+        // add LocalIndexDataColumnRef
+        for (LocalIndexDataColumnRef sourceColumnRef : 
visitor.localIndexColumnRefSet) {
+            PColumn column = new 
ProjectedColumn(sourceColumnRef.getColumn().getName(), 
+                    sourceColumnRef.getColumn().getFamilyName(), position++, 
+                    sourceColumnRef.getColumn().isNullable(), sourceColumnRef);
+            projectedColumns.add(column);
+        }
+        
+        return PTableImpl.makePTable(table.getTenantId(), 
table.getSchemaName(), table.getName(), PTableType.PROJECTED,
+                table.getIndexState(), table.getTimeStamp(), 
table.getSequenceNumber(), table.getPKName(),
+                table.getBucketNum(), projectedColumns, 
table.getParentSchemaName(),
+                table.getParentName(), table.getIndexes(), 
table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
+                table.isWALDisabled(), table.isMultiTenant(), 
table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
+                table.getIndexType());
+    }
+
+    public static PTable createProjectedTable(TableRef tableRef, 
List<ColumnRef> sourceColumnRefs, boolean retainPKColumns) throws SQLException {
+        PTable table = tableRef.getTable();
+        boolean hasSaltingColumn = retainPKColumns && table.getBucketNum() != 
null;
+        List<PColumn> projectedColumns = new ArrayList<PColumn>();
+        int position = hasSaltingColumn ? 1 : 0;
+        for (int i = position; i < sourceColumnRefs.size(); i++) {
+            ColumnRef sourceColumnRef = sourceColumnRefs.get(i);
+            PColumn sourceColumn = sourceColumnRef.getColumn();
+            String colName = sourceColumn.getName().getString();
+            String aliasedName = tableRef.getTableAlias() == null ? 
+                      SchemaUtil.getColumnName(table.getName().getString(), 
colName) 
+                    : SchemaUtil.getColumnName(tableRef.getTableAlias(), 
colName);
+
+            PColumn column = new 
ProjectedColumn(PNameFactory.newName(aliasedName), 
+                    retainPKColumns && SchemaUtil.isPKColumn(sourceColumn) ? 
+                            null : 
PNameFactory.newName(TupleProjector.VALUE_COLUMN_FAMILY), 
+                    position++, sourceColumn.isNullable(), sourceColumnRef);
+            projectedColumns.add(column);
+        }
+        return PTableImpl.makePTable(table.getTenantId(), 
PROJECTED_TABLE_SCHEMA, table.getName(), PTableType.PROJECTED,
+                    null, table.getTimeStamp(), table.getSequenceNumber(), 
table.getPKName(),
+                    retainPKColumns ? table.getBucketNum() : null, 
projectedColumns, null,
+                    null, Collections.<PTable>emptyList(), 
table.isImmutableRows(), Collections.<PName>emptyList(), null, null,
+                    table.isWALDisabled(), table.isMultiTenant(), 
table.getStoreNulls(), table.getViewType(), table.getViewIndexId(),
+                    null);
+    }
+
+    // For extracting column references from single select statement
+    private static class ColumnRefVisitor extends 
StatelessTraverseAllParseNodeVisitor {
+        private final StatementContext context;
+        private final Set<ColumnRef> columnRefSet;
+        private final Set<LocalIndexDataColumnRef> localIndexColumnRefSet;
+        
+        private ColumnRefVisitor(StatementContext context) {
+            this.context = context;
+            this.columnRefSet = new HashSet<ColumnRef>();
+            this.localIndexColumnRefSet = new 
HashSet<LocalIndexDataColumnRef>();
+        }
+
+        @Override
+        public Void visit(ColumnParseNode node) throws SQLException {
+            try {
+                
columnRefSet.add(context.getResolver().resolveColumn(node.getSchemaName(), 
node.getTableName(), node.getName()));
+            } catch (ColumnNotFoundException e) {
+                if (context.getCurrentTable().getTable().getIndexType() == 
IndexType.LOCAL) {
+                    try {
+                        localIndexColumnRefSet.add(new 
LocalIndexDataColumnRef(context, node.getName()));
+                    } catch (ColumnFamilyNotFoundException c) {
+                        throw e;
+                    }
+                } else {
+                    throw e;
+                }
+            }
+            return null;
+        }        
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
index b21cc2f..8a76564 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/UpsertCompiler.java
@@ -419,11 +419,11 @@ public class UpsertCompiler {
                     // Pass scan through if same table in upsert and select so 
that projection is computed correctly
                     // Use optimizer to choose the best plan
                     try {
-                        QueryCompiler compiler = new QueryCompiler(statement, 
select, selectResolver, targetColumns, parallelIteratorFactoryToBe, new 
SequenceManager(statement));
+                        QueryCompiler compiler = new QueryCompiler(statement, 
select, selectResolver, targetColumns, parallelIteratorFactoryToBe, new 
SequenceManager(statement), false);
                         queryPlanToBe = compiler.compile();
                         // This is post-fix: if the tableRef is a projected 
table, this means there are post-processing 
                         // steps and parallelIteratorFactory did not take 
effect.
-                        if (queryPlanToBe.getTableRef().getTable().getType() 
== PTableType.JOIN || queryPlanToBe.getTableRef().getTable().getType() == 
PTableType.SUBQUERY) {
+                        if (queryPlanToBe.getTableRef().getTable().getType() 
== PTableType.PROJECTED || queryPlanToBe.getTableRef().getTable().getType() == 
PTableType.SUBQUERY) {
                             parallelIteratorFactoryToBe = null;
                         }
                     } catch (MetaDataEntityNotFoundException e) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java 
b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
index 406b567..9631850 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/WhereCompiler.java
@@ -145,7 +145,7 @@ public class WhereCompiler {
             expression = AndExpression.create(filters);
         }
         
-        if (context.getCurrentTable().getTable().getType() != PTableType.JOIN 
&& context.getCurrentTable().getTable().getType() != PTableType.SUBQUERY) {
+        if (context.getCurrentTable().getTable().getType() != 
PTableType.PROJECTED && context.getCurrentTable().getTable().getType() != 
PTableType.SUBQUERY) {
             expression = WhereOptimizer.pushKeyExpressionsToScan(context, 
statement, expression, extractedNodes);
         }
         setScanFilter(context, statement, expression, 
whereCompiler.disambiguateWithFamily, hashJoinOptimization);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
index 69cdcb6..25ac408 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/BaseScannerRegionObserver.java
@@ -29,6 +29,7 @@ import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.KeyValue;
 import org.apache.hadoop.hbase.KeyValue.Type;
+import org.apache.hadoop.hbase.client.Result;
 import org.apache.hadoop.hbase.client.Scan;
 import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
@@ -47,6 +48,8 @@ import org.apache.phoenix.schema.KeyValueSchema;
 import org.apache.phoenix.schema.StaleRegionBoundaryCacheException;
 import org.apache.phoenix.schema.ValueBitSet;
 import org.apache.phoenix.schema.tuple.MultiKeyValueTuple;
+import org.apache.phoenix.schema.tuple.ResultTuple;
+import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.ScanUtil;
 import org.apache.phoenix.util.ServerUtil;
@@ -216,9 +219,10 @@ abstract public class BaseScannerRegionObserver extends 
BaseRegionObserver {
             final RegionScanner s, final int offset, final Scan scan,
             final ColumnReference[] dataColumns, final TupleProjector 
tupleProjector,
             final HRegion dataRegion, final IndexMaintainer indexMaintainer,
-            final byte[][] viewConstants, final ImmutableBytesWritable ptr) {
+            final byte[][] viewConstants, final TupleProjector projector,
+            final ImmutableBytesWritable ptr) {
         return getWrappedScanner(c, s, null, null, offset, scan, dataColumns, 
tupleProjector,
-                dataRegion, indexMaintainer, viewConstants, null, null, ptr);
+                dataRegion, indexMaintainer, viewConstants, null, null, 
projector, ptr);
     }
     
     /**
@@ -241,7 +245,8 @@ abstract public class BaseScannerRegionObserver extends 
BaseRegionObserver {
             final ColumnReference[] dataColumns, final TupleProjector 
tupleProjector,
             final HRegion dataRegion, final IndexMaintainer indexMaintainer,
             final byte[][] viewConstants, final KeyValueSchema kvSchema, 
-            final ValueBitSet kvSchemaBitSet, final ImmutableBytesWritable 
ptr) {
+            final ValueBitSet kvSchemaBitSet, final TupleProjector projector,
+            final ImmutableBytesWritable ptr) {
         return new RegionScanner() {
 
             @Override
@@ -303,6 +308,11 @@ abstract public class BaseScannerRegionObserver extends 
BaseRegionObserver {
                         IndexUtil.wrapResultUsingOffset(c, result, offset, 
dataColumns,
                             tupleProjector, dataRegion, indexMaintainer, 
viewConstants, ptr);
                     }
+                    if (projector != null) {
+                        Tuple tuple = projector.projectResults(new 
ResultTuple(Result.create(result)));
+                        result.clear();
+                        result.add(tuple.getValue(0));
+                    }
                     // There is a scanattribute set to retrieve the specific 
array element
                     return next;
                 } catch (Throwable t) {
@@ -325,6 +335,11 @@ abstract public class BaseScannerRegionObserver extends 
BaseRegionObserver {
                         IndexUtil.wrapResultUsingOffset(c, result, offset, 
dataColumns,
                             tupleProjector, dataRegion, indexMaintainer, 
viewConstants, ptr);
                     }
+                    if (projector != null) {
+                        Tuple tuple = projector.projectResults(new 
ResultTuple(Result.create(result)));
+                        result.clear();
+                        result.add(tuple.getValue(0));
+                    }
                     // There is a scanattribute set to retrieve the specific 
array element
                     return next;
                 } catch (Throwable t) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/GroupedAggregateRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/GroupedAggregateRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/GroupedAggregateRegionObserver.java
index 0984b06..1f1ba36 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/GroupedAggregateRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/GroupedAggregateRegionObserver.java
@@ -131,7 +131,10 @@ public class GroupedAggregateRegionObserver extends 
BaseScannerRegionObserver {
         HRegion dataRegion = null;
         byte[][] viewConstants = null;
         ColumnReference[] dataColumns = 
IndexUtil.deserializeDataTableColumnsToJoin(scan);
-        if (ScanUtil.isLocalIndex(scan)) {
+
+        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
+        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
+        if (ScanUtil.isLocalIndex(scan) || (j == null && p != null)) {
             if (dataColumns != null) {
                 tupleProjector = IndexUtil.getTupleProjector(scan, 
dataColumns);
                 dataRegion = IndexUtil.getDataRegion(c.getEnvironment());
@@ -140,12 +143,10 @@ public class GroupedAggregateRegionObserver extends 
BaseScannerRegionObserver {
             ImmutableBytesWritable tempPtr = new ImmutableBytesWritable();
             innerScanner =
                     getWrappedScanner(c, innerScanner, offset, scan, 
dataColumns, tupleProjector, 
-                            dataRegion, indexMaintainers == null ? null : 
indexMaintainers.get(0), viewConstants, tempPtr);
+                            dataRegion, indexMaintainers == null ? null : 
indexMaintainers.get(0), viewConstants, p, tempPtr);
         } 
 
-        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
-        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
-        if (p != null || j != null) {
+        if (j != null) {
             innerScanner =
                     new HashJoinRegionScanner(innerScanner, p, j, 
ScanUtil.getTenantId(scan),
                             c.getEnvironment());

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/HashJoinRegionScanner.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/HashJoinRegionScanner.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/HashJoinRegionScanner.java
index 176520e..cdfc771 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/HashJoinRegionScanner.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/HashJoinRegionScanner.java
@@ -70,39 +70,37 @@ public class HashJoinRegionScanner implements RegionScanner 
{
         this.hasMore = true;
         this.count = 0;
         this.limit = Long.MAX_VALUE;
-        if (joinInfo != null) {
-            for (JoinType type : joinInfo.getJoinTypes()) {
-                if (type != JoinType.Inner && type != JoinType.Left && type != 
JoinType.Semi && type != JoinType.Anti)
-                    throw new DoNotRetryIOException("Got join type '" + type + 
"'. Expect only INNER or LEFT with hash-joins.");
-            }
-            if (joinInfo.getLimit() != null) {
-                this.limit = joinInfo.getLimit();
-            }
-            int count = joinInfo.getJoinIds().length;
-            this.tempTuples = new List[count];
-            this.hashCaches = new HashCache[count];
-            this.tempSrcBitSet = new ValueBitSet[count];
-            TenantCache cache = GlobalCache.getTenantCache(env, tenantId);
-            for (int i = 0; i < count; i++) {
-                ImmutableBytesPtr joinId = joinInfo.getJoinIds()[i];
-                if (joinId.getLength() == 0) { // semi-join optimized into 
skip-scan
-                    hashCaches[i] = null;
-                    tempSrcBitSet[i] = null;
-                    tempTuples[i] = null;
-                    continue;
-                }
-                HashCache hashCache = (HashCache)cache.getServerCache(joinId);
-                if (hashCache == null)
-                    throw new DoNotRetryIOException("Could not find hash cache 
for joinId: " 
-                            + Bytes.toString(joinId.get(), joinId.getOffset(), 
joinId.getLength()) 
-                            + ". The cache might have expired and have been 
removed.");
-                hashCaches[i] = hashCache;
-                tempSrcBitSet[i] = 
ValueBitSet.newInstance(joinInfo.getSchemas()[i]);
-            }
-            if (this.projector != null) {
-                this.tempDestBitSet = 
ValueBitSet.newInstance(joinInfo.getJoinedSchema());
-                this.projector.setValueBitSet(tempDestBitSet);
+        for (JoinType type : joinInfo.getJoinTypes()) {
+            if (type != JoinType.Inner && type != JoinType.Left && type != 
JoinType.Semi && type != JoinType.Anti)
+                throw new DoNotRetryIOException("Got join type '" + type + "'. 
Expect only INNER or LEFT with hash-joins.");
+        }
+        if (joinInfo.getLimit() != null) {
+            this.limit = joinInfo.getLimit();
+        }
+        int count = joinInfo.getJoinIds().length;
+        this.tempTuples = new List[count];
+        this.hashCaches = new HashCache[count];
+        this.tempSrcBitSet = new ValueBitSet[count];
+        TenantCache cache = GlobalCache.getTenantCache(env, tenantId);
+        for (int i = 0; i < count; i++) {
+            ImmutableBytesPtr joinId = joinInfo.getJoinIds()[i];
+            if (joinId.getLength() == 0) { // semi-join optimized into 
skip-scan
+                hashCaches[i] = null;
+                tempSrcBitSet[i] = null;
+                tempTuples[i] = null;
+                continue;
             }
+            HashCache hashCache = (HashCache)cache.getServerCache(joinId);
+            if (hashCache == null)
+                throw new DoNotRetryIOException("Could not find hash cache for 
joinId: " 
+                        + Bytes.toString(joinId.get(), joinId.getOffset(), 
joinId.getLength()) 
+                        + ". The cache might have expired and have been 
removed.");
+            hashCaches[i] = hashCache;
+            tempSrcBitSet[i] = 
ValueBitSet.newInstance(joinInfo.getSchemas()[i]);
+        }
+        if (this.projector != null) {
+            this.tempDestBitSet = 
ValueBitSet.newInstance(joinInfo.getJoinedSchema());
+            this.projector.setValueBitSet(tempDestBitSet);
         }
     }
     
@@ -111,13 +109,11 @@ public class HashJoinRegionScanner implements 
RegionScanner {
             return;
         
         Tuple tuple = new ResultTuple(Result.create(result));
-        if (joinInfo == null || joinInfo.forceProjection()) {
+        // For backward compatibility. In new versions, 
HashJoinInfo.forceProjection()
+        // always returns true.
+        if (joinInfo.forceProjection()) {
             tuple = projector.projectResults(tuple);
         }
-        if (joinInfo == null) {
-            resultQueue.offer(tuple);
-            return;
-        }
         
         if (hasBatchLimit)
             throw new UnsupportedOperationException("Cannot support join 
operations in scans with limit");
@@ -147,7 +143,7 @@ public class HashJoinRegionScanner implements RegionScanner 
{
                 }
             } else {
                 KeyValueSchema schema = joinInfo.getJoinedSchema();
-                if (!joinInfo.forceProjection()) {
+                if (!joinInfo.forceProjection()) { // backward compatibility
                     tuple = projector.projectResults(tuple);
                 }
                 resultQueue.offer(tuple);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ScanRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ScanRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ScanRegionObserver.java
index 9270495..ddde407 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ScanRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ScanRegionObserver.java
@@ -199,15 +199,16 @@ public class ScanRegionObserver extends 
BaseScannerRegionObserver {
             indexMaintainer = indexMaintainers.get(0);
             viewConstants = IndexUtil.deserializeViewConstantsFromScan(scan);
         }
+        
+        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
+        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
         innerScanner =
                 getWrappedScanner(c, innerScanner, arrayKVRefs, arrayFuncRefs, 
offset, scan,
                     dataColumns, tupleProjector, dataRegion, indexMaintainer, 
viewConstants,
-                    kvSchema, kvSchemaBitSet, ptr);
+                    kvSchema, kvSchemaBitSet, j == null ? p : null, ptr);
 
-        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
-        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
         final ImmutableBytesWritable tenantId = ScanUtil.getTenantId(scan);
-        if (p != null || j != null) {
+        if (j != null) {
             innerScanner = new HashJoinRegionScanner(innerScanner, p, j, 
tenantId, c.getEnvironment());
         }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
index 71c4dc6..72a0a64 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/UngroupedAggregateRegionObserver.java
@@ -214,7 +214,9 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
         byte[][] viewConstants = null;
         ColumnReference[] dataColumns = 
IndexUtil.deserializeDataTableColumnsToJoin(scan);
         boolean localIndexScan = ScanUtil.isLocalIndex(scan);
-        if (localIndexScan && !isDelete) {
+        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
+        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
+        if ((localIndexScan && !isDelete) || (j == null && p != null)) {
             if (dataColumns != null) {
                 tupleProjector = IndexUtil.getTupleProjector(scan, 
dataColumns);
                 dataRegion = IndexUtil.getDataRegion(c.getEnvironment());
@@ -223,12 +225,10 @@ public class UngroupedAggregateRegionObserver extends 
BaseScannerRegionObserver{
             ImmutableBytesWritable tempPtr = new ImmutableBytesWritable();
             theScanner =
                     getWrappedScanner(c, theScanner, offset, scan, 
dataColumns, tupleProjector, 
-                            dataRegion, indexMaintainers == null ? null : 
indexMaintainers.get(0), viewConstants, tempPtr);
+                            dataRegion, indexMaintainers == null ? null : 
indexMaintainers.get(0), viewConstants, p, tempPtr);
         } 
         
-        final TupleProjector p = 
TupleProjector.deserializeProjectorFromScan(scan);
-        final HashJoinInfo j = HashJoinInfo.deserializeHashJoinFromScan(scan);
-        if (p != null || j != null)  {
+        if (j != null)  {
             theScanner = new HashJoinRegionScanner(theScanner, p, j, 
ScanUtil.getTenantId(scan), c.getEnvironment());
         }
         

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjector.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjector.java 
b/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjector.java
index 77682e4..a4728e9 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjector.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjector.java
@@ -32,19 +32,23 @@ import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.hadoop.io.WritableUtils;
 import org.apache.phoenix.compile.ColumnProjector;
-import org.apache.phoenix.compile.JoinCompiler.ProjectedPTableWrapper;
 import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.ExpressionType;
 import org.apache.phoenix.schema.KeyValueSchema;
 import org.apache.phoenix.schema.KeyValueSchema.KeyValueSchemaBuilder;
 import org.apache.phoenix.schema.PColumn;
+import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableType;
+import org.apache.phoenix.schema.ProjectedColumn;
 import org.apache.phoenix.schema.ValueBitSet;
 import org.apache.phoenix.schema.tuple.BaseTuple;
 import org.apache.phoenix.schema.tuple.Tuple;
 import org.apache.phoenix.util.KeyValueUtil;
 import org.apache.phoenix.util.SchemaUtil;
 
+import com.google.common.base.Preconditions;
+
 public class TupleProjector {    
     public static final byte[] VALUE_COLUMN_FAMILY = Bytes.toBytes("_v");
     public static final byte[] VALUE_COLUMN_QUALIFIER = new byte[0];
@@ -70,16 +74,16 @@ public class TupleProjector {
         valueSet = ValueBitSet.newInstance(schema);
     }
     
-    public TupleProjector(ProjectedPTableWrapper projected) {
-       List<PColumn> columns = projected.getTable().getColumns();
-       expressions = new Expression[columns.size() - 
projected.getTable().getPKColumns().size()];
-       // we do not count minNullableIndex for we might do later merge.
+    public TupleProjector(PTable projectedTable) {
+        Preconditions.checkArgument(projectedTable.getType() == 
PTableType.PROJECTED);
+       List<PColumn> columns = projectedTable.getColumns();
+       this.expressions = new Expression[columns.size() - 
projectedTable.getPKColumns().size()];
        KeyValueSchemaBuilder builder = new KeyValueSchemaBuilder(0);
        int i = 0;
-        for (PColumn column : projected.getTable().getColumns()) {
+        for (PColumn column : columns) {
                if (!SchemaUtil.isPKColumn(column)) {
                        builder.addField(column);
-                       expressions[i++] = 
projected.getSourceExpression(column);
+                       expressions[i++] = ((ProjectedColumn) 
column).getSourceColumnRef().newColumnExpression();
                }
         }
         schema = builder.build();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/join/HashJoinInfo.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/join/HashJoinInfo.java 
b/phoenix-core/src/main/java/org/apache/phoenix/join/HashJoinInfo.java
index ad96061..ea78671 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/join/HashJoinInfo.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/join/HashJoinInfo.java
@@ -50,10 +50,9 @@ public class HashJoinInfo {
     private int[] fieldPositions;
     private Expression postJoinFilterExpression;
     private Integer limit;
-    private boolean forceProjection;
 
-    public HashJoinInfo(PTable joinedTable, ImmutableBytesPtr[] joinIds, 
List<Expression>[] joinExpressions, JoinType[] joinTypes, boolean[] 
earlyEvaluation, PTable[] tables, int[] fieldPositions, Expression 
postJoinFilterExpression, Integer limit, boolean forceProjection) {
-       this(buildSchema(joinedTable), joinIds, joinExpressions, joinTypes, 
earlyEvaluation, buildSchemas(tables), fieldPositions, 
postJoinFilterExpression, limit, forceProjection);
+    public HashJoinInfo(PTable joinedTable, ImmutableBytesPtr[] joinIds, 
List<Expression>[] joinExpressions, JoinType[] joinTypes, boolean[] 
earlyEvaluation, PTable[] tables, int[] fieldPositions, Expression 
postJoinFilterExpression, Integer limit) {
+       this(buildSchema(joinedTable), joinIds, joinExpressions, joinTypes, 
earlyEvaluation, buildSchemas(tables), fieldPositions, 
postJoinFilterExpression, limit);
     }
 
     private static KeyValueSchema[] buildSchemas(PTable[] tables) {
@@ -76,7 +75,7 @@ public class HashJoinInfo {
         return builder.build();
     }
 
-    private HashJoinInfo(KeyValueSchema joinedSchema, ImmutableBytesPtr[] 
joinIds, List<Expression>[] joinExpressions, JoinType[] joinTypes, boolean[] 
earlyEvaluation, KeyValueSchema[] schemas, int[] fieldPositions, Expression 
postJoinFilterExpression, Integer limit, boolean forceProjection) {
+    private HashJoinInfo(KeyValueSchema joinedSchema, ImmutableBytesPtr[] 
joinIds, List<Expression>[] joinExpressions, JoinType[] joinTypes, boolean[] 
earlyEvaluation, KeyValueSchema[] schemas, int[] fieldPositions, Expression 
postJoinFilterExpression, Integer limit) {
        this.joinedSchema = joinedSchema;
        this.joinIds = joinIds;
         this.joinExpressions = joinExpressions;
@@ -86,7 +85,6 @@ public class HashJoinInfo {
         this.fieldPositions = fieldPositions;
         this.postJoinFilterExpression = postJoinFilterExpression;
         this.limit = limit;
-        this.forceProjection = forceProjection;
     }
 
     public KeyValueSchema getJoinedSchema() {
@@ -124,15 +122,11 @@ public class HashJoinInfo {
     public Integer getLimit() {
         return limit;
     }
-
-    /*
-     * If the LHS table is a sub-select, we always do projection, since
-     * the ON expressions reference only projected columns.
-     */
+    
     public boolean forceProjection() {
-        return forceProjection;
+        return true;
     }
-
+ 
     public static void serializeHashJoinIntoScan(Scan scan, HashJoinInfo 
joinInfo) {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         try {
@@ -159,7 +153,7 @@ public class HashJoinInfo {
                 WritableUtils.writeVInt(output, -1);
             }
             WritableUtils.writeVInt(output, joinInfo.limit == null ? -1 : 
joinInfo.limit);
-            output.writeBoolean(joinInfo.forceProjection);
+            output.writeBoolean(true);
             scan.setAttribute(HASH_JOIN, stream.toByteArray());
         } catch (IOException e) {
             throw new RuntimeException(e);
@@ -216,17 +210,16 @@ public class HashJoinInfo {
                 postJoinFilterExpression.readFields(input);
             }
             int limit = -1;
-            boolean forceProjection = false;
             // Read these and ignore if we don't find them as they were not
             // present in Apache Phoenix 3.0.0 release. This allows a newer
             // 3.1 server to work with an older 3.0 client without force
             // both to be upgraded in lock step.
             try {
                 limit = WritableUtils.readVInt(input);
-                forceProjection = input.readBoolean();
+                input.readBoolean(); // discarded info in new versions
             } catch (EOFException ignore) {
             }
-            return new HashJoinInfo(joinedSchema, joinIds, joinExpressions, 
joinTypes, earlyEvaluation, schemas, fieldPositions, postJoinFilterExpression, 
limit >= 0 ? limit : null, forceProjection);
+            return new HashJoinInfo(joinedSchema, joinIds, joinExpressions, 
joinTypes, earlyEvaluation, schemas, fieldPositions, postJoinFilterExpression, 
limit >= 0 ? limit : null);
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/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 a51723b..382bba5 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
@@ -113,14 +113,13 @@ public class QueryOptimizer {
         SelectStatement select = (SelectStatement)dataPlan.getStatement();
         // Exit early if we have a point lookup as we can't get better than 
that
         if (!useIndexes 
-                || select.isJoin() 
-                || dataPlan.getContext().getResolver().getTables().size() > 1
-                || select.getInnerSelectStatement() != null
                 || (dataPlan.getContext().getScanRanges().isPointLookup() && 
stopAtBestPlan)) {
             return Collections.singletonList(dataPlan);
         }
-        PTable dataTable = dataPlan.getTableRef().getTable();
-        List<PTable>indexes = Lists.newArrayList(dataTable.getIndexes());
+        // For single query tuple projection, indexes are inherited from the 
original table to the projected
+        // table; otherwise not. So we pass projected table here, which is 
enough to tell if this is from a
+        // single query or a part of join query.
+        List<PTable>indexes = 
Lists.newArrayList(dataPlan.getContext().getResolver().getTables().get(0).getTable().getIndexes());
         if (indexes.isEmpty() || dataPlan.isDegenerate() || 
dataPlan.getTableRef().hasDynamicCols() || 
select.getHint().hasHint(Hint.NO_INDEX)) {
             return Collections.singletonList(dataPlan);
         }
@@ -138,7 +137,7 @@ public class QueryOptimizer {
             targetColumns = targetDatums;
         }
         
-        SelectStatement translatedIndexSelect = 
IndexStatementRewriter.translate(select, dataPlan.getContext().getResolver());
+        SelectStatement translatedIndexSelect = 
IndexStatementRewriter.translate(select, 
FromCompiler.getResolver(dataPlan.getTableRef()));
         List<QueryPlan> plans = Lists.newArrayListWithExpectedSize(1 + 
indexes.size());
         plans.add(dataPlan);
         QueryPlan hintedPlan = getHintedQueryPlan(statement, 
translatedIndexSelect, indexes, targetColumns, parallelIteratorFactory, plans);
@@ -230,12 +229,14 @@ public class QueryOptimizer {
         TableNode table = FACTORY.namedTable(alias, FACTORY.table(schemaName, 
tableName));
         SelectStatement indexSelect = FACTORY.select(select, table);
         ColumnResolver resolver = 
FromCompiler.getResolverForQuery(indexSelect, statement.getConnection());
+        // We will or will not do tuple projection according to the data plan.
+        boolean isProjected = 
dataPlan.getContext().getResolver().getTables().get(0).getTable().getType() == 
PTableType.PROJECTED;
         // Check index state of now potentially updated index table to make 
sure it's active
         if 
(PIndexState.ACTIVE.equals(resolver.getTables().get(0).getTable().getIndexState()))
 {
             try {
                // translate nodes that match expressions that are indexed to 
the associated column parse node
                 indexSelect = ParseNodeRewriter.rewrite(indexSelect, new  
IndexExpressionParseNodeRewriter(index, statement.getConnection()));
-                QueryCompiler compiler = new QueryCompiler(statement, 
indexSelect, resolver, targetColumns, parallelIteratorFactory, 
dataPlan.getContext().getSequenceManager());
+                QueryCompiler compiler = new QueryCompiler(statement, 
indexSelect, resolver, targetColumns, parallelIteratorFactory, 
dataPlan.getContext().getSequenceManager(), isProjected);
                 
                 QueryPlan plan = compiler.compile();
                 // If query doesn't have where clause and some of columns to 
project are missing
@@ -267,7 +268,7 @@ public class QueryOptimizer {
                 ParseNode where = dataSelect.getWhere();
                 if (isHinted && where != null) {
                     StatementContext context = new StatementContext(statement, 
resolver);
-                    WhereConditionRewriter whereRewriter = new 
WhereConditionRewriter(dataPlan.getContext().getResolver(), context);
+                    WhereConditionRewriter whereRewriter = new 
WhereConditionRewriter(FromCompiler.getResolver(dataPlan.getTableRef()), 
context);
                     where = where.accept(whereRewriter);
                     if (where != null) {
                         PTable dataTable = dataPlan.getTableRef().getTable();
@@ -301,7 +302,7 @@ public class QueryOptimizer {
                         query = SubqueryRewriter.transform(query, 
queryResolver, statement.getConnection());
                         queryResolver = 
FromCompiler.getResolverForQuery(query, statement.getConnection());
                         query = StatementNormalizer.normalize(query, 
queryResolver);
-                        QueryPlan plan = new QueryCompiler(statement, query, 
queryResolver).compile();
+                        QueryPlan plan = new QueryCompiler(statement, query, 
queryResolver, targetColumns, parallelIteratorFactory, 
dataPlan.getContext().getSequenceManager(), isProjected).compile();
                         return plan;
                     }
                 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/ColumnRef.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/ColumnRef.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/ColumnRef.java
index c6dd1f4..76f6218 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/ColumnRef.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/ColumnRef.java
@@ -105,7 +105,7 @@ public class ColumnRef {
                     displayName);
         }
         
-        if (table.getType() == PTableType.JOIN || table.getType() == 
PTableType.SUBQUERY) {
+        if (table.getType() == PTableType.PROJECTED || table.getType() == 
PTableType.SUBQUERY) {
                return new ProjectedColumnExpression(column, table, 
displayName);
         }
        

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/LocalIndexDataColumnRef.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/LocalIndexDataColumnRef.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/LocalIndexDataColumnRef.java
index 62ef431..270c66d 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/LocalIndexDataColumnRef.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/LocalIndexDataColumnRef.java
@@ -20,16 +20,13 @@ package org.apache.phoenix.schema;
 import java.sql.SQLException;
 import java.util.Set;
 
-import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.expression.ColumnExpression;
 import org.apache.phoenix.expression.ProjectedColumnExpression;
 import org.apache.phoenix.parse.ParseNodeFactory;
 import org.apache.phoenix.parse.TableName;
-import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.util.IndexUtil;
-import org.apache.phoenix.util.SchemaUtil;
 
 public class LocalIndexDataColumnRef extends ColumnRef {
     final private int position;
@@ -62,11 +59,7 @@ public class LocalIndexDataColumnRef extends ColumnRef {
 
     @Override
     public ColumnExpression newColumnExpression(boolean 
schemaNameCaseSensitive, boolean colNameCaseSensitive) {
-        PTable table = this.getTable();
-        PColumn column = this.getColumn();
-        // TODO: util for this or store in member variable
-        byte[] defaultFamily = table.getDefaultFamilyName() == null ? 
QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES : 
table.getDefaultFamilyName().getBytes();
-        String displayName = 
SchemaUtil.getColumnDisplayName(Bytes.compareTo(defaultFamily, 
column.getFamilyName().getBytes()) == 0  ? null : 
column.getFamilyName().getBytes(), column.getName().getBytes());
+        String displayName = this.getTableRef().getColumnDisplayName(this, 
schemaNameCaseSensitive, colNameCaseSensitive);
         return new ProjectedColumnExpression(this.getColumn(), columns, 
position, displayName);
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 831616b..e133433 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -764,7 +764,7 @@ public class MetaDataClient {
                 String tableName = getFullTableName(dataTableRef);
                 String query = "SELECT count(*) FROM " + tableName;
                 final QueryPlan plan = statement.compileQuery(query);
-                TableRef tableRef = 
plan.getContext().getResolver().getTables().get(0);
+                TableRef tableRef = plan.getTableRef();
                 // Set attribute on scan that UngroupedAggregateRegionObserver 
will switch on.
                 // We'll detect that this attribute was set the server-side 
and write the index
                 // rows per region as a result. The value of the attribute 
will be our persisted

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableType.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableType.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableType.java
index 23ba829..019c0e1 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableType.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableType.java
@@ -27,7 +27,7 @@ public enum PTableType {
     TABLE("u", "TABLE"),
     VIEW("v", "VIEW"),
     INDEX("i", "INDEX"),
-    JOIN("j", "JOIN"),
+    PROJECTED("p", "PROJECTED"),
     SUBQUERY("q", "SUBQUERY"); 
 
     private final PName value;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/ProjectedColumn.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/ProjectedColumn.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/ProjectedColumn.java
new file mode 100644
index 0000000..19dd1c1
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/ProjectedColumn.java
@@ -0,0 +1,59 @@
+/*
+ * 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.schema;
+
+public class ProjectedColumn extends DelegateColumn {
+    
+    private final PName name;
+    private final PName familyName;
+    private final int position;
+    private final boolean nullable;
+    private final ColumnRef sourceColumnRef;
+
+    public ProjectedColumn(PName name, PName familyName, int position, boolean 
nullable, ColumnRef sourceColumnRef) {
+        super(sourceColumnRef.getColumn());
+        this.name = name;
+        this.familyName = familyName;
+        this.position = position;
+        this.nullable = nullable;
+        this.sourceColumnRef = sourceColumnRef;
+    }
+    
+    @Override
+    public PName getName() {
+        return name;
+    }
+    
+    public PName getFamilyName() {
+        return familyName;
+    }
+    
+    @Override
+    public int getPosition() {
+        return position;
+    }
+    
+    @Override
+    public boolean isNullable() {
+        return nullable;
+    }
+
+    public ColumnRef getSourceColumnRef() {
+        return sourceColumnRef;
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
index b64912b..bd88770 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/TableRef.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.schema;
 
 import org.apache.hadoop.hbase.HConstants;
+import org.apache.phoenix.compile.TupleProjectionCompiler;
 import org.apache.phoenix.query.QueryConstants;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.SchemaUtil;
@@ -73,9 +74,10 @@ public class TableRef {
         String cq = null;       
         PColumn column = ref.getColumn();
         String name = column.getName().getString();
-        boolean isIndex = table.getType() == PTableType.INDEX;
-        if (table.getType() == PTableType.JOIN || table.getType() == 
PTableType.SUBQUERY) {
-            cq = column.getName().getString();
+        boolean isIndex = IndexUtil.isIndexColumn(name);
+        if ((table.getType() == PTableType.PROJECTED && 
TupleProjectionCompiler.PROJECTED_TABLE_SCHEMA.equals(table.getSchemaName()))
+                || table.getType() == PTableType.SUBQUERY) {
+            cq = name;
         }
         else if (SchemaUtil.isPKColumn(column)) {
             cq = isIndex ? IndexUtil.getDataColumnName(name) : name;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/3f829751/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereCompilerTest.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereCompilerTest.java 
b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereCompilerTest.java
index 01f28ae..3a012fb 100644
--- 
a/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereCompilerTest.java
+++ 
b/phoenix-core/src/test/java/org/apache/phoenix/compile/WhereCompilerTest.java
@@ -619,7 +619,7 @@ public class WhereCompilerTest extends 
BaseConnectionlessQueryTest {
                     pointRange(tenantId1),
                     pointRange(tenantId2),
                     pointRange(tenantId3))),
-                
plan.getContext().getResolver().getTables().get(0).getTable().getRowKeySchema()),
+                plan.getTableRef().getTable().getRowKeySchema()),
             filter);
     }
 
@@ -642,7 +642,7 @@ public class WhereCompilerTest extends 
BaseConnectionlessQueryTest {
                     pointRange(tenantId1),
                     pointRange(tenantId2),
                     pointRange(tenantId3))),
-                
plan.getContext().getResolver().getTables().get(0).getTable().getRowKeySchema()),
+                plan.getTableRef().getTable().getRowKeySchema()),
             filter);
 
         byte[] startRow = PVarchar.INSTANCE.toBytes(tenantId1);
@@ -705,7 +705,7 @@ public class WhereCompilerTest extends 
BaseConnectionlessQueryTest {
                         true,
                         Bytes.toBytes(entityId2),
                         true))),
-                
plan.getContext().getResolver().getTables().get(0).getTable().getRowKeySchema()),
+                plan.getTableRef().getTable().getRowKeySchema()),
             filter);
     }
 

Reply via email to