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/03 05:56:46 UTC

phoenix git commit: Remove PhoenixProjectScanMergeRule; Inject projection to server side plans whenever possible; Make aggregate+join merged server plans as in original Phoenix planning

Repository: phoenix
Updated Branches:
  refs/heads/calcite b01bdd172 -> f6ff11710


Remove PhoenixProjectScanMergeRule; Inject projection to server side plans whenever possible; Make aggregate+join merged server plans as in original Phoenix planning


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/f6ff1171
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/f6ff1171
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/f6ff1171

Branch: refs/heads/calcite
Commit: f6ff117104d78c09ab691535c9cc0c40003e8636
Parents: b01bdd1
Author: maryannxue <we...@intel.com>
Authored: Thu Apr 2 23:56:26 2015 -0400
Committer: maryannxue <we...@intel.com>
Committed: Thu Apr 2 23:56:26 2015 -0400

----------------------------------------------------------------------
 .../org/apache/phoenix/calcite/CalciteTest.java | 41 ++++++++++++++-----
 .../apache/phoenix/calcite/CalciteUtils.java    | 10 +++++
 .../phoenix/calcite/PhoenixAggregate.java       | 36 ++++++++--------
 .../apache/phoenix/calcite/PhoenixFilter.java   |  5 +++
 .../calcite/PhoenixFilterScanMergeRule.java     |  2 +-
 .../org/apache/phoenix/calcite/PhoenixJoin.java | 13 ++++--
 .../apache/phoenix/calcite/PhoenixProject.java  | 43 ++++++++++++++------
 .../calcite/PhoenixProjectScanMergeRule.java    | 37 -----------------
 .../org/apache/phoenix/calcite/PhoenixRel.java  | 23 ++++++++++-
 .../calcite/PhoenixRelImplementorImpl.java      |  5 ++-
 .../org/apache/phoenix/calcite/PhoenixSort.java |  5 +++
 .../apache/phoenix/calcite/PhoenixTable.java    |  2 +-
 .../phoenix/calcite/PhoenixTableScan.java       | 41 ++++++-------------
 .../calcite/PhoenixToEnumerableConverter.java   | 26 +++++++++---
 .../apache/phoenix/calcite/PhoenixUnion.java    |  5 +++
 .../apache/phoenix/calcite/PhoenixValues.java   |  5 +++
 .../GroupedAggregateRegionObserver.java         |  3 +-
 .../coprocessor/HashJoinRegionScanner.java      |  8 +++-
 .../phoenix/coprocessor/ScanRegionObserver.java |  3 +-
 .../UngroupedAggregateRegionObserver.java       |  3 +-
 .../phoenix/execute/TupleProjectionPlan.java    | 12 ------
 .../apache/phoenix/execute/TupleProjector.java  | 17 +++++++-
 .../java/org/apache/phoenix/util/TestUtil.java  |  4 +-
 23 files changed, 210 insertions(+), 139 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
index 333315c..f9e00ce 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteTest.java
@@ -201,7 +201,8 @@ public class CalciteTest extends BaseClientManagedTimeIT {
     @Test public void testProject() throws Exception {
         start().sql("select entity_id, a_string, organization_id from aTable where a_string = 'a'")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')], project=[[$1, $2, $0]])\n")
+                           "  PhoenixProject(ENTITY_ID=[$1], A_STRING=[$2], ORGANIZATION_ID=[$0])\n" +
+                           "    PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')])\n")
                 .resultIs(new Object[][] {
                           {"00A123122312312", "a", "00D300000000XHP"}, 
                           {"00A223122312312", "a", "00D300000000XHP"}, 
@@ -215,8 +216,10 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                 .explainIs("PhoenixToEnumerableConverter\n" +
                            "  PhoenixProject(ENTITY_ID=[$4], A_STRING=[$2], ORGANIZATION_ID=[$3])\n" +
                            "    PhoenixJoin(condition=[AND(=($4, $1), =($3, $0))], joinType=[inner])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, ATABLE]], project=[[$0, $1, $2]])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')], project=[[$0, $1, $2]])\n")
+                           "      PhoenixProject(ORGANIZATION_ID=[$0], ENTITY_ID=[$1], A_STRING=[$2])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, ATABLE]])\n" +
+                           "      PhoenixProject(ORGANIZATION_ID=[$0], ENTITY_ID=[$1], A_STRING=[$2])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[=($2, 'a')])\n")
                 .resultIs(new Object[][] {
                           {"00A123122312312", "a", "00D300000000XHP"}, 
                           {"00A223122312312", "a", "00D300000000XHP"}, 
@@ -228,8 +231,10 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                 .explainIs("PhoenixToEnumerableConverter\n" +
                            "  PhoenixProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
                            "    PhoenixJoin(condition=[=($2, $3)], joinType=[inner])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, ITEMTABLE]], project=[[$0, $1, $5]])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, SUPPLIERTABLE]], project=[[$0, $1]])\n")
+                           "      PhoenixProject(item_id=[$0], NAME=[$1], supplier_id=[$5])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, ITEMTABLE]])\n" +
+                           "      PhoenixProject(supplier_id=[$0], NAME=[$1])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, SUPPLIERTABLE]])\n")
                 .resultIs(new Object[][] {
                           {"0000000001", "T1", "0000000001", "S1"}, 
                           {"0000000002", "T2", "0000000001", "S1"}, 
@@ -238,6 +243,17 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                           {"0000000005", "T5", "0000000005", "S5"},
                           {"0000000006", "T6", "0000000006", "S6"}})
                 .close();
+        
+        start().sql("SELECT * FROM " + JOIN_ITEM_TABLE_FULL_NAME + " item JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" AND supp.name = 'S5'")
+                .explainIs("PhoenixToEnumerableConverter\n" +
+                           "  PhoenixProject(item_id=[$0], NAME=[$1], PRICE=[$2], DISCOUNT1=[$3], DISCOUNT2=[$4], supplier_id=[$5], DESCRIPTION=[$6], supplier_id0=[$7], NAME0=[$8], PHONE=[$9], ADDRESS=[$10], LOC_ID=[$11])\n" +
+                           "    PhoenixJoin(condition=[=($5, $7)], joinType=[inner])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, ITEMTABLE]])\n" +
+                           "      PhoenixProject(supplier_id=[$0], NAME=[$1], PHONE=[$2], ADDRESS=[$3], LOC_ID=[$4], $f5=[CAST($1):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, SUPPLIERTABLE]], filter=[=(CAST($1):VARCHAR(2) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL, 'S5')])\n")
+                .resultIs(new Object[][] {
+                          {"0000000005", "T5", 500, 8, 15, "0000000005", "Item T5", "0000000005", "S5", "888-888-5555", "505 YYY Street", "10005"}})
+                .close();
     }
     
     @Test public void testMultiJoin() throws Exception {
@@ -282,7 +298,8 @@ public class CalciteTest extends BaseClientManagedTimeIT {
         start().sql("select a_string, count(entity_id) from atable group by a_string")
                 .explainIs("PhoenixToEnumerableConverter\n" +
                            "  PhoenixAggregate(group=[{0}], EXPR$1=[COUNT()])\n" +
-                           "    PhoenixTableScan(table=[[phoenix, ATABLE]], project=[[$2]])\n")
+                           "    PhoenixProject(A_STRING=[$2])\n" +
+                           "      PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
                 .resultIs(new Object[][] {
                           {"a", 4L},
                           {"b", 4L},
@@ -293,7 +310,8 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                 .explainIs("PhoenixToEnumerableConverter\n" +
                            "  PhoenixProject(EXPR$0=[$1], A_STRING=[$0])\n" +
                            "    PhoenixAggregate(group=[{0}], EXPR$0=[COUNT()])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, ATABLE]], project=[[$2]])\n")
+                           "      PhoenixProject(A_STRING=[$2])\n" +
+                           "        PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
                 .resultIs(new Object[][] {
                           {4L, "a"},
                           {4L, "b"},
@@ -305,8 +323,10 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                            "  PhoenixAggregate(group=[{0}], EXPR$1=[COUNT()])\n" +
                            "    PhoenixProject(NAME=[$1])\n" +
                            "      PhoenixJoin(condition=[=($0, $2)], joinType=[inner])\n" +
-                           "        PhoenixTableScan(table=[[phoenix, SUPPLIERTABLE]], project=[[$0, $1]])\n" +
-                           "        PhoenixTableScan(table=[[phoenix, ITEMTABLE]], project=[[$5]])\n")
+                           "        PhoenixProject(supplier_id=[$0], NAME=[$1])\n" +
+                           "          PhoenixTableScan(table=[[phoenix, SUPPLIERTABLE]])\n" +
+                           "        PhoenixProject(supplier_id=[$5])\n" +
+                           "          PhoenixTableScan(table=[[phoenix, ITEMTABLE]])\n")
                 .resultIs(new Object[][] {
                           {"S1", 2L},
                           {"S2", 2L},
@@ -326,7 +346,8 @@ public class CalciteTest extends BaseClientManagedTimeIT {
                           "          PhoenixJoin(condition=[=($6, $2)], joinType=[inner])\n" +
                           "            PhoenixTableScan(table=[[phoenix, ORDERTABLE]])\n" +
                           "            PhoenixAggregate(group=[{0}])\n" +
-                          "              PhoenixTableScan(table=[[phoenix, ORDERTABLE]], project=[[$2]])\n")
+                          "              PhoenixProject(item_id=[$2])\n" +
+                          "                PhoenixTableScan(table=[[phoenix, ORDERTABLE]])\n")
                .close();
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
index 4962bb5..8146d6d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/CalciteUtils.java
@@ -89,6 +89,16 @@ public class CalciteUtils {
 			}
 			
 		});
+		EXPRESSION_MAP.put(SqlKind.CAST, new ExpressionFactory() {
+
+            @Override
+            public Expression newExpression(RexNode node,
+                    Implementor implementor) {
+                // TODO replace with real implementation
+                return toExpression(((RexCall) node).getOperands().get(0), implementor);
+            }
+		    
+		});
 	}
 	
     private static final Map<String, FunctionFactory> FUNCTION_MAP = Maps

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
index 0c620c8..c3d4982 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixAggregate.java
@@ -14,8 +14,6 @@ import org.apache.calcite.rel.core.Aggregate;
 import org.apache.calcite.rel.core.AggregateCall;
 import org.apache.calcite.util.ImmutableBitSet;
 import org.apache.hadoop.hbase.client.Scan;
-import org.apache.phoenix.compile.ColumnProjector;
-import org.apache.phoenix.compile.ExpressionProjector;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.compile.RowProjector;
@@ -41,6 +39,7 @@ import org.apache.phoenix.parse.SelectStatement;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.RowKeyValueAccessor;
 import org.apache.phoenix.schema.TableRef;
+
 import com.google.common.collect.Lists;
 
 /**
@@ -48,7 +47,6 @@ import com.google.common.collect.Lists;
  * relational expression in Phoenix.
  */
 public class PhoenixAggregate extends Aggregate implements PhoenixRel {
-    private static double SERVER_AGGREGATE_FACTOR = 0.2;
     
     public PhoenixAggregate(RelOptCluster cluster, RelTraitSet traits, RelNode child, boolean indicator, ImmutableBitSet groupSet, List<ImmutableBitSet> groupSets, List<AggregateCall> aggCalls) throws InvalidRelException {
         super(cluster, traits, child, indicator, groupSet, groupSets, aggCalls);
@@ -70,8 +68,8 @@ public class PhoenixAggregate extends Aggregate implements PhoenixRel {
     @Override
     public RelOptCost computeSelfCost(RelOptPlanner planner) {
         RelOptCost cost = super.computeSelfCost(planner);
-        if (isServerAggregate()) {
-            cost = cost.multiplyBy(SERVER_AGGREGATE_FACTOR);
+        if (isServerAggregateDoable()) {
+            cost = cost.multiplyBy(SERVER_FACTOR);
         }
         return cost.multiplyBy(PHOENIX_FACTOR);
     }
@@ -147,9 +145,9 @@ public class PhoenixAggregate extends Aggregate implements PhoenixRel {
         SelectStatement select = SelectStatement.SELECT_STAR;
         QueryPlan aggPlan;
         if (basePlan == null) {
-            aggPlan = new ClientAggregatePlan(context, select, tableRef, implementor.createRowProjector(), null, null, OrderBy.EMPTY_ORDER_BY, groupBy, null, plan);
+            aggPlan = new ClientAggregatePlan(context, select, tableRef, RowProjector.EMPTY_PROJECTOR, null, null, OrderBy.EMPTY_ORDER_BY, groupBy, null, plan);
         } else {
-            aggPlan = new AggregatePlan(context, select, basePlan.getTableRef(), implementor.createRowProjector(), null, OrderBy.EMPTY_ORDER_BY, null, groupBy, null);
+            aggPlan = new AggregatePlan(context, select, basePlan.getTableRef(), RowProjector.EMPTY_PROJECTOR, null, OrderBy.EMPTY_ORDER_BY, null, groupBy, null);
             if (plan instanceof HashJoinPlan) {        
                 HashJoinPlan hashJoinPlan = (HashJoinPlan) plan;
                 aggPlan = HashJoinPlan.create(select, aggPlan, hashJoinPlan.getJoinInfo(), hashJoinPlan.getSubPlans());
@@ -169,15 +167,7 @@ public class PhoenixAggregate extends Aggregate implements PhoenixRel {
         TupleProjector tupleProjector = implementor.project(exprs);
         PTable projectedTable = implementor.createProjectedTable();
         implementor.setTableRef(new TableRef(projectedTable));
-        return new TupleProjectionPlan(aggPlan, tupleProjector, null, implementor.createRowProjector());
-    }
-    
-    public boolean isServerAggregate() {
-        RelNode rel = getInput();
-        if (rel instanceof RelSubset) {
-            rel = ((RelSubset) rel).getBest();
-        }
-        return (rel instanceof PhoenixTableScan) || (rel instanceof PhoenixJoin && ((PhoenixJoin) rel).isHashJoinDoable());        
+        return new TupleProjectionPlan(aggPlan, tupleProjector, null);
     }
     
     private static int getMinNullableIndex(List<SingleAggregateFunction> aggFuncs, boolean isUngroupedAggregation) {
@@ -192,4 +182,18 @@ public class PhoenixAggregate extends Aggregate implements PhoenixRel {
         return minNullableIndex;
     }
     
+    private boolean isServerAggregateDoable() {
+        RelNode rel = getInput();
+        if (rel instanceof RelSubset) {
+            rel = ((RelSubset) rel).getBest();
+        }
+        
+        return rel instanceof PhoenixRel && ((PhoenixRel) rel).getPlanType() != PlanType.CLIENT_SERVER;
+    }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.CLIENT_SERVER;
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
index 8163682..8925ead 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilter.java
@@ -41,4 +41,9 @@ public class PhoenixFilter extends Filter implements PhoenixRel {
         return new ClientScanPlan(plan.getContext(), plan.getStatement(), plan.getTableRef(),
                 plan.getProjector(), null, expr, OrderBy.EMPTY_ORDER_BY, plan);
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.CLIENT_SERVER;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
index d35abad..808fa99 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixFilterScanMergeRule.java
@@ -31,6 +31,6 @@ public class PhoenixFilterScanMergeRule extends RelOptRule {
         assert scan.filter == null : "predicate should have ensured no filter";
         call.transformTo(new PhoenixTableScan(scan.getCluster(),
                 scan.getTraitSet(), scan.getTable(),
-                filter.getCondition(), scan.projects, scan.getRowType()));
+                filter.getCondition()));
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
index a1384a6..c316b5d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixJoin.java
@@ -78,7 +78,7 @@ public class PhoenixJoin extends Join implements PhoenixRel {
         JoinInfo joinInfo = JoinInfo.of(left, right, getCondition());
         List<Expression> leftExprs = Lists.<Expression> newArrayList();
         List<Expression> rightExprs = Lists.<Expression> newArrayList();
-        implementor.pushContext(new ImplementorContext(implementor.getCurrentContext().isRetainPKColumns()));
+        implementor.pushContext(new ImplementorContext(implementor.getCurrentContext().isRetainPKColumns(), true));
         QueryPlan leftPlan = implementor.visitInput(0, left);
         PTable leftTable = implementor.getTableRef().getTable();
         for (Iterator<Integer> iter = joinInfo.leftKeys.iterator(); iter.hasNext();) {
@@ -89,7 +89,7 @@ public class PhoenixJoin extends Join implements PhoenixRel {
             leftExprs.add(LiteralExpression.newConstant(0));
         }
         implementor.popContext();
-        implementor.pushContext(new ImplementorContext(false));
+        implementor.pushContext(new ImplementorContext(false, true));
         QueryPlan rightPlan = implementor.visitInput(1, right);
         PTable rightTable = implementor.getTableRef().getTable();
         for (Iterator<Integer> iter = joinInfo.rightKeys.iterator(); iter.hasNext();) {
@@ -123,13 +123,13 @@ public class PhoenixJoin extends Join implements PhoenixRel {
         return HashJoinPlan.create(SelectStatement.SELECT_STAR, leftPlan, hashJoinInfo, new HashJoinPlan.HashSubPlan[] {new HashJoinPlan.HashSubPlan(0, rightPlan, rightExprs, false, null, null)});
     }
     
-    public boolean isHashJoinDoable() {
+    private boolean isHashJoinDoable() {
         // TODO check memory limit
         RelNode rel = getLeft();
         if (rel instanceof RelSubset) {
             rel = ((RelSubset) rel).getBest();
         }
-        return (rel instanceof PhoenixTableScan) && getJoinType() != JoinRelType.RIGHT;
+        return (rel instanceof PhoenixRel && ((PhoenixRel) rel).getPlanType() == PlanType.SERVER_ONLY_FLAT) && getJoinType() != JoinRelType.RIGHT;
     }
     
     private JoinType convertJoinType(JoinRelType type) {
@@ -152,4 +152,9 @@ public class PhoenixJoin extends Join implements PhoenixRel {
         
         return ret;
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.SERVER_ONLY_COMPLEX;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
index 6b82f42..4f08968 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProject.java
@@ -1,31 +1,23 @@
 package org.apache.phoenix.calcite;
 
-import java.sql.SQLException;
-import java.util.Collections;
 import java.util.List;
 
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
 import org.apache.calcite.plan.RelOptPlanner;
 import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
-import org.apache.hadoop.hbase.HConstants;
 import org.apache.phoenix.compile.QueryPlan;
-import org.apache.phoenix.coprocessor.MetaDataProtocol;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.execute.ScanPlan;
 import org.apache.phoenix.execute.TupleProjectionPlan;
 import org.apache.phoenix.execute.TupleProjector;
 import org.apache.phoenix.expression.Expression;
-import org.apache.phoenix.schema.KeyValueSchema;
-import org.apache.phoenix.schema.PColumn;
-import org.apache.phoenix.schema.PColumnImpl;
-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.TableRef;
 
 import com.google.common.collect.Lists;
@@ -46,13 +38,19 @@ public class PhoenixProject extends Project implements PhoenixRel {
     }
 
     public RelOptCost computeSelfCost(RelOptPlanner planner) {
-        return super.computeSelfCost(planner).multiplyBy(PHOENIX_FACTOR);
+        RelOptCost cost = super.computeSelfCost(planner);
+        if (getPlanType() != PlanType.CLIENT_SERVER) {
+            cost = cost.multiplyBy(SERVER_FACTOR);
+        }
+        return cost.multiplyBy(PHOENIX_FACTOR);
     }
 
     @Override
     public QueryPlan implement(Implementor implementor) {
         assert getConvention() == getInput().getConvention();
+        implementor.pushContext(new ImplementorContext(implementor.getCurrentContext().isRetainPKColumns(), false));
         QueryPlan plan = implementor.visitInput(0, (PhoenixRel) getInput());
+        implementor.popContext();
         
         List<Expression> exprs = Lists.newArrayList();
         for (RexNode project : getProjects()) {
@@ -61,6 +59,25 @@ public class PhoenixProject extends Project implements PhoenixRel {
         TupleProjector tupleProjector = implementor.project(exprs);
         PTable projectedTable = implementor.createProjectedTable();
         implementor.setTableRef(new TableRef(projectedTable));
-        return new TupleProjectionPlan(plan, tupleProjector, null, implementor.createRowProjector());
+        
+        boolean isScan = plan instanceof ScanPlan;
+        if (getPlanType() == PlanType.CLIENT_SERVER 
+                || TupleProjector.hasProjector(plan.getContext().getScan(), isScan))        
+            return new TupleProjectionPlan(plan, tupleProjector, null);
+        
+        TupleProjector.serializeProjectorIntoScan(plan.getContext().getScan(), tupleProjector, isScan);
+        return plan;
+    }
+
+    @Override
+    public PlanType getPlanType() {
+        RelNode rel = getInput();
+        if (rel instanceof RelSubset) {
+            rel = ((RelSubset) rel).getBest();
+        }
+        // TODO this is based on the assumption that there is no two Project 
+        // in a row and Project can be pushed down to the input node if it is 
+        // a server plan.
+        return !(rel instanceof PhoenixRel) ? PlanType.CLIENT_SERVER : ((PhoenixRel) rel).getPlanType();
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProjectScanMergeRule.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProjectScanMergeRule.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProjectScanMergeRule.java
deleted file mode 100644
index d28159d..0000000
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixProjectScanMergeRule.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.apache.phoenix.calcite;
-
-import org.apache.calcite.plan.RelOptRule;
-import org.apache.calcite.plan.RelOptRuleCall;
-import org.apache.calcite.rel.core.Project;
-
-import com.google.common.base.Predicate;
-
-public class PhoenixProjectScanMergeRule extends RelOptRule {
-
-    /** Predicate that returns true if a table scan has no project. */
-    private static final Predicate<PhoenixTableScan> NO_PROJECT =
-        new Predicate<PhoenixTableScan>() {
-            @Override
-            public boolean apply(PhoenixTableScan phoenixTableScan) {
-                return phoenixTableScan.projects == null;
-            }
-        };
-
-    public static final PhoenixProjectScanMergeRule INSTANCE = new PhoenixProjectScanMergeRule();
-
-    private PhoenixProjectScanMergeRule() {
-        super(
-            operand(Project.class,
-                operand(PhoenixTableScan.class, null, NO_PROJECT, any())));
-    }
-
-    @Override
-    public void onMatch(RelOptRuleCall call) {
-        Project project = call.rel(0);
-        PhoenixTableScan scan = call.rel(1);
-        assert scan.projects == null : "predicate should have ensured no project";
-        call.transformTo(new PhoenixTableScan(scan.getCluster(),
-                scan.getTraitSet(), scan.getTable(),
-                scan.filter, project.getProjects(), project.getRowType()));
-    }
-}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
index d89cdab..f5943da 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRel.java
@@ -29,18 +29,39 @@ public interface PhoenixRel extends RelNode {
    */
   double PHOENIX_FACTOR = 0.5;
 
+  /** Relative cost of server plan versus client plan.
+   *
+   * <p>Multiply by the value (which is less than unity), and you will get a cheaper cost.
+   * Server is cheaper.
+   */
+  double SERVER_FACTOR = 0.2;
+  
+  enum PlanType {
+      SERVER_ONLY_FLAT,
+      SERVER_ONLY_COMPLEX,
+      CLIENT_SERVER,
+  }
+  
+  PlanType getPlanType();
+
   QueryPlan implement(Implementor implementor);
   
   class ImplementorContext {
       private boolean retainPKColumns;
+      private boolean forceProject;
       
-      public ImplementorContext(boolean retainPKColumns) {
+      public ImplementorContext(boolean retainPKColumns, boolean forceProject) {
           this.retainPKColumns = retainPKColumns;
+          this.forceProject = forceProject;
       }
       
       public boolean isRetainPKColumns() {
           return this.retainPKColumns;
       }
+      
+      public boolean forceProject() {
+          return this.forceProject;
+      }
   }
 
   /** Holds context for an traversal over a tree of relational expressions

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
index 67e1fd0..ec0277a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixRelImplementorImpl.java
@@ -16,6 +16,7 @@ import org.apache.phoenix.coprocessor.MetaDataProtocol;
 import org.apache.phoenix.execute.TupleProjector;
 import org.apache.phoenix.expression.ColumnExpression;
 import org.apache.phoenix.expression.Expression;
+import org.apache.phoenix.parse.ParseNodeFactory;
 import org.apache.phoenix.schema.ColumnRef;
 import org.apache.phoenix.schema.KeyValueSchema;
 import org.apache.phoenix.schema.PColumn;
@@ -35,7 +36,7 @@ class PhoenixRelImplementorImpl implements PhoenixRel.Implementor {
 	
 	public PhoenixRelImplementorImpl() {
 	    this.contextStack = new Stack<ImplementorContext>();
-	    pushContext(new ImplementorContext(true));
+	    pushContext(new ImplementorContext(true, false));
 	}
 	
     @Override
@@ -105,8 +106,8 @@ class PhoenixRelImplementorImpl implements PhoenixRel.Implementor {
         KeyValueSchema.KeyValueSchemaBuilder builder = new KeyValueSchema.KeyValueSchemaBuilder(0);
         List<PColumn> columns = Lists.<PColumn>newArrayList();
         for (int i = 0; i < exprs.size(); i++) {
+            String name = ParseNodeFactory.createTempAlias();
             Expression expr = exprs.get(i);
-            String name = expr.toString();
             builder.addField(expr);
             columns.add(new PColumnImpl(PNameFactory.newName(name), PNameFactory.newName(TupleProjector.VALUE_COLUMN_FAMILY),
                     expr.getDataType(), expr.getMaxLength(), expr.getScale(), expr.isNullable(),

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
index 4eccf5a..6d11231 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixSort.java
@@ -29,4 +29,9 @@ public class PhoenixSort extends Sort implements PhoenixRel {
         implementor.visitInput(0, (PhoenixRel) getInput());
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.CLIENT_SERVER;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
index b2fba0a..e18a57a 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTable.java
@@ -70,7 +70,7 @@ public class PhoenixTable extends AbstractTable implements TranslatableTable {
     @Override
     public RelNode toRel(RelOptTable.ToRelContext context, RelOptTable relOptTable) {
         final RelOptCluster cluster = context.getCluster();
-        return new PhoenixTableScan(cluster, cluster.traitSetOf(PhoenixRel.CONVENTION), relOptTable, null, null, null);
+        return new PhoenixTableScan(cluster, cluster.traitSetOf(PhoenixRel.CONVENTION), relOptTable, null);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
index e21d28f..8b437bc 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixTableScan.java
@@ -13,7 +13,6 @@ import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.RelWriter;
 import org.apache.calcite.rel.core.TableScan;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
-import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.client.Scan;
@@ -46,15 +45,10 @@ import com.google.common.collect.Lists;
  */
 public class PhoenixTableScan extends TableScan implements PhoenixRel {
     public final RexNode filter;
-    public final List<RexNode> projects;
 
-    protected PhoenixTableScan(RelOptCluster cluster, RelTraitSet traits, RelOptTable table, RexNode filter, List<RexNode> projects, RelDataType rowType) {
+    protected PhoenixTableScan(RelOptCluster cluster, RelTraitSet traits, RelOptTable table, RexNode filter) {
         super(cluster, traits, table);
         this.filter = filter;
-        this.projects = projects;
-        if (rowType != null) {
-            this.rowType = rowType;
-        }
     }
 
     @Override
@@ -70,14 +64,12 @@ public class PhoenixTableScan extends TableScan implements PhoenixRel {
             planner.addRule(rule);
         }
         planner.addRule(PhoenixFilterScanMergeRule.INSTANCE);
-        planner.addRule(PhoenixProjectScanMergeRule.INSTANCE);
     }
 
     @Override
     public RelWriter explainTerms(RelWriter pw) {
         return super.explainTerms(pw)
-            .itemIf("filter", filter, filter != null)
-            .itemIf("project", projects, projects != null);
+            .itemIf("filter", filter, filter != null);
     }
 
     @Override
@@ -87,10 +79,6 @@ public class PhoenixTableScan extends TableScan implements PhoenixRel {
             final Double selectivity = RelMetadataQuery.getSelectivity(this, filter);
             cost = cost.multiplyBy(selectivity);
         }
-        if (projects != null) {
-            final double projectFieldRatio = ((double) projects.size()) / getRowType().getFieldCount();
-            cost = cost.multiplyBy(projectFieldRatio);
-        }
         return cost;
     }
     
@@ -117,24 +105,16 @@ public class PhoenixTableScan extends TableScan implements PhoenixRel {
                 WhereCompiler.setScanFilter(context, select, filterExpr, true, false);
             }
             projectAllColumnFamilies(context.getScan(), phoenixTable.getTable());
-            TupleProjector tupleProjector;
-            if (projects == null) {
-                tupleProjector = createTupleProjector(implementor, phoenixTable.getTable());
-            } else {
-                List<Expression> exprs = Lists.newArrayList();
-                for (RexNode project : this.projects) {
-                    exprs.add(CalciteUtils.toExpression(project, implementor));
-                }
-                tupleProjector = implementor.project(exprs);
+            if (implementor.getCurrentContext().forceProject()) {
+                TupleProjector tupleProjector = createTupleProjector(implementor, phoenixTable.getTable());
+                TupleProjector.serializeProjectorIntoScan(context.getScan(), tupleProjector);
+                PTable projectedTable = implementor.createProjectedTable();
+                implementor.setTableRef(new TableRef(projectedTable));
             }
-            TupleProjector.serializeProjectorIntoScan(context.getScan(), tupleProjector);
-            PTable projectedTable = implementor.createProjectedTable();
-            implementor.setTableRef(new TableRef(projectedTable));
-            RowProjector rowProjector = implementor.createRowProjector();
             Integer limit = null;
             OrderBy orderBy = OrderBy.EMPTY_ORDER_BY;
             ParallelIteratorFactory iteratorFactory = null;
-            return new ScanPlan(context, select, tableRef, rowProjector, limit, orderBy, iteratorFactory, true);
+            return new ScanPlan(context, select, tableRef, RowProjector.EMPTY_PROJECTOR, limit, orderBy, iteratorFactory, true);
         } catch (SQLException e) {
             throw new RuntimeException(e);
         }
@@ -161,4 +141,9 @@ public class PhoenixTableScan extends TableScan implements PhoenixRel {
             scan.addFamily(family.getName().getBytes());
         }
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.SERVER_ONLY_FLAT;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
index d1750e3..537e748 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixToEnumerableConverter.java
@@ -1,5 +1,6 @@
 package org.apache.phoenix.calcite;
 
+import java.sql.SQLException;
 import java.util.List;
 
 import org.apache.calcite.adapter.enumerable.EnumerableRel;
@@ -20,12 +21,11 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.convert.ConverterImpl;
 import org.apache.calcite.rel.type.RelDataType;
-import org.apache.calcite.tools.FrameworkConfig;
-import org.apache.calcite.tools.Frameworks;
-import org.apache.calcite.tools.Planner;
-import org.apache.calcite.tools.Program;
-import org.apache.calcite.tools.Programs;
+import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.QueryPlan;
+import org.apache.phoenix.compile.RowProjector;
+import org.apache.phoenix.execute.DelegateQueryPlan;
+import org.apache.phoenix.iterate.ResultIterator;
 
 /**
  * Scan of a Phoenix table.
@@ -73,7 +73,21 @@ public class PhoenixToEnumerableConverter extends ConverterImpl implements Enume
     
     static QueryPlan makePlan(PhoenixRel rel) {
         final PhoenixRel.Implementor phoenixImplementor = new PhoenixRelImplementorImpl();
-        return phoenixImplementor.visitInput(0, rel);
+        final QueryPlan plan = phoenixImplementor.visitInput(0, rel);
+        return new DelegateQueryPlan(plan) {
+            @Override
+            public ResultIterator iterator() throws SQLException {
+                return delegate.iterator();
+            }
+            @Override
+            public ExplainPlan getExplainPlan() throws SQLException {
+                return delegate.getExplainPlan();
+            }
+            @Override
+            public RowProjector getProjector() {
+                return phoenixImplementor.createRowProjector();
+            }
+        };
     }
 
     static Expression stash(EnumerableRelImplementor implementor, Object o, Class clazz) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
index cc76334..c824246 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixUnion.java
@@ -34,4 +34,9 @@ public class PhoenixUnion extends Union implements PhoenixRel {
         }
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.CLIENT_SERVER;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
index 6e01abb..92bc676 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixValues.java
@@ -34,4 +34,9 @@ public class PhoenixValues extends Values implements PhoenixRel {
     public QueryPlan implement(Implementor implementor) {
         throw new UnsupportedOperationException();
     }
+
+    @Override
+    public PlanType getPlanType() {
+        return PlanType.SERVER_ONLY_FLAT;
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/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 1f1ba36..180c895 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
@@ -147,8 +147,9 @@ public class GroupedAggregateRegionObserver extends BaseScannerRegionObserver {
         } 
 
         if (j != null) {
+            TupleProjector postJoinProjector = TupleProjector.deserializeProjectorFromScan(scan, false);
             innerScanner =
-                    new HashJoinRegionScanner(innerScanner, p, j, ScanUtil.getTenantId(scan),
+                    new HashJoinRegionScanner(innerScanner, p, postJoinProjector, j, ScanUtil.getTenantId(scan),
                             c.getEnvironment());
         }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/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 cdfc771..f969ce9 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
@@ -60,11 +60,13 @@ public class HashJoinRegionScanner implements RegionScanner {
     private List<Tuple>[] tempTuples;
     private ValueBitSet tempDestBitSet;
     private ValueBitSet[] tempSrcBitSet;
+    private final TupleProjector postJoinProjector;
     
     @SuppressWarnings("unchecked")
-    public HashJoinRegionScanner(RegionScanner scanner, TupleProjector projector, HashJoinInfo joinInfo, ImmutableBytesWritable tenantId, RegionCoprocessorEnvironment env) throws IOException {
+    public HashJoinRegionScanner(RegionScanner scanner, TupleProjector projector, TupleProjector postJoinProjector, HashJoinInfo joinInfo, ImmutableBytesWritable tenantId, RegionCoprocessorEnvironment env) throws IOException {
         this.scanner = scanner;
         this.projector = projector;
+        this.postJoinProjector = postJoinProjector;
         this.joinInfo = joinInfo;
         this.resultQueue = new LinkedList<Tuple>();
         this.hasMore = true;
@@ -224,6 +226,10 @@ public class HashJoinRegionScanner implements RegionScanner {
             return false;
         
         Tuple tuple = resultQueue.poll();
+        // post-join projection
+        if (postJoinProjector != null) {
+            tuple = postJoinProjector.projectResults(tuple);
+        }
         for (int i = 0; i < tuple.size(); i++) {
             results.add(tuple.getValue(i));
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/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 ddde407..9b1ea0d 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
@@ -209,7 +209,8 @@ public class ScanRegionObserver extends BaseScannerRegionObserver {
 
         final ImmutableBytesWritable tenantId = ScanUtil.getTenantId(scan);
         if (j != null) {
-            innerScanner = new HashJoinRegionScanner(innerScanner, p, j, tenantId, c.getEnvironment());
+            TupleProjector postJoinProjector = TupleProjector.deserializeProjectorFromScan(scan, false);
+            innerScanner = new HashJoinRegionScanner(innerScanner, p, postJoinProjector, j, tenantId, c.getEnvironment());
         }
 
         final OrderedResultIterator iterator = deserializeFromScan(scan,innerScanner);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/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 72a0a64..0cf2320 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
@@ -229,7 +229,8 @@ public class UngroupedAggregateRegionObserver extends BaseScannerRegionObserver{
         } 
         
         if (j != null)  {
-            theScanner = new HashJoinRegionScanner(theScanner, p, j, ScanUtil.getTenantId(scan), c.getEnvironment());
+            TupleProjector postJoinProjector = TupleProjector.deserializeProjectorFromScan(scan, false);
+            theScanner = new HashJoinRegionScanner(theScanner, p, postJoinProjector, j, ScanUtil.getTenantId(scan), c.getEnvironment());
         }
         
         int batchSize = 0;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjectionPlan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjectionPlan.java b/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjectionPlan.java
index b2eba2c..c9cbd15 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjectionPlan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/execute/TupleProjectionPlan.java
@@ -22,7 +22,6 @@ import java.util.List;
 
 import org.apache.phoenix.compile.ExplainPlan;
 import org.apache.phoenix.compile.QueryPlan;
-import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.iterate.DelegateResultIterator;
 import org.apache.phoenix.iterate.FilterResultIterator;
@@ -34,23 +33,12 @@ import com.google.common.collect.Lists;
 public class TupleProjectionPlan extends DelegateQueryPlan {
     private final TupleProjector tupleProjector;
     private final Expression postFilter;
-    private final RowProjector rowProjector;
 
     public TupleProjectionPlan(QueryPlan plan, TupleProjector tupleProjector, Expression postFilter) {
-        this(plan, tupleProjector, postFilter, plan.getProjector());
-    }
-    
-    public TupleProjectionPlan(QueryPlan plan, TupleProjector tupleProjector, Expression postFilter, RowProjector rowProjector) {
         super(plan);
         if (tupleProjector == null) throw new IllegalArgumentException("tupleProjector is null");
         this.tupleProjector = tupleProjector;
         this.postFilter = postFilter;
-        this.rowProjector = rowProjector;
-    }
-    
-    @Override
-    public RowProjector getProjector() {
-        return this.rowProjector;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/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 a4728e9..7ec147f 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
@@ -54,6 +54,7 @@ public class TupleProjector {
     public static final byte[] VALUE_COLUMN_QUALIFIER = new byte[0];
     
     private static final String SCAN_PROJECTOR = "scanProjector";
+    private static final String POST_JOIN_PROJECTOR = "postJoinProjector";
     
     private final KeyValueSchema schema;
     private final Expression[] expressions;
@@ -100,7 +101,15 @@ public class TupleProjector {
         this.valueSet = bitSet;
     }
     
+    public static boolean hasProjector(Scan scan, boolean scanProjector) {
+        return scan.getAttribute(scanProjector ? SCAN_PROJECTOR : POST_JOIN_PROJECTOR) != null;
+    }
+    
     public static void serializeProjectorIntoScan(Scan scan, TupleProjector projector) {
+        serializeProjectorIntoScan(scan, projector, true);
+    }
+    
+    public static void serializeProjectorIntoScan(Scan scan, TupleProjector projector, boolean scanProjector) {
         ByteArrayOutputStream stream = new ByteArrayOutputStream();
         try {
             DataOutputStream output = new DataOutputStream(stream);
@@ -111,7 +120,7 @@ public class TupleProjector {
             	WritableUtils.writeVInt(output, ExpressionType.valueOf(projector.expressions[i]).ordinal());
             	projector.expressions[i].write(output);
             }
-            scan.setAttribute(SCAN_PROJECTOR, stream.toByteArray());
+            scan.setAttribute(scanProjector ? SCAN_PROJECTOR : POST_JOIN_PROJECTOR, stream.toByteArray());
         } catch (IOException e) {
             throw new RuntimeException(e);
         } finally {
@@ -125,7 +134,11 @@ public class TupleProjector {
     }
     
     public static TupleProjector deserializeProjectorFromScan(Scan scan) {
-        byte[] proj = scan.getAttribute(SCAN_PROJECTOR);
+        return deserializeProjectorFromScan(scan, true);
+    }
+    
+    public static TupleProjector deserializeProjectorFromScan(Scan scan, boolean scanProjector) {
+        byte[] proj = scan.getAttribute(scanProjector ? SCAN_PROJECTOR : POST_JOIN_PROJECTOR);
         if (proj == null) {
             return null;
         }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/f6ff1171/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
index 2b7a62b..802f803 100644
--- a/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
+++ b/phoenix-core/src/test/java/org/apache/phoenix/util/TestUtil.java
@@ -199,8 +199,8 @@ public class TestUtil {
     public static final String JOIN_ITEM_TABLE_FULL_NAME = JOIN_ITEM_TABLE; //'"' + JOIN_SCHEMA + "\".\"" + JOIN_ITEM_TABLE + '"';
     public static final String JOIN_SUPPLIER_TABLE_FULL_NAME = JOIN_SUPPLIER_TABLE; //'"' + JOIN_SCHEMA + "\".\"" + JOIN_SUPPLIER_TABLE + '"';
     public static final String JOIN_COITEM_TABLE_FULL_NAME = '"' + JOIN_SCHEMA + "\".\"" + JOIN_COITEM_TABLE + '"';
-    public static final String JOIN_ORDER_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_ORDER_TABLE;
-    public static final String JOIN_CUSTOMER_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_CUSTOMER_TABLE;
+    public static final String JOIN_ORDER_TABLE_DISPLAY_NAME = JOIN_ORDER_TABLE.toUpperCase();
+    public static final String JOIN_CUSTOMER_TABLE_DISPLAY_NAME = JOIN_CUSTOMER_TABLE.toUpperCase();
     public static final String JOIN_ITEM_TABLE_DISPLAY_NAME = JOIN_ITEM_TABLE.toUpperCase();
     public static final String JOIN_SUPPLIER_TABLE_DISPLAY_NAME = JOIN_SUPPLIER_TABLE.toUpperCase();
     public static final String JOIN_COITEM_TABLE_DISPLAY_NAME = JOIN_SCHEMA + "." + JOIN_COITEM_TABLE;