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 2017/02/04 06:00:08 UTC

[2/2] phoenix git commit: PHOENIX-3309 Divide query planning into multiple stages

PHOENIX-3309 Divide query planning into multiple stages


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

Branch: refs/heads/calcite
Commit: 36a31bf904ea0f12dd6aa7e3ab5e89ef8af427c2
Parents: 229f4c7
Author: maryannxue <ma...@gmail.com>
Authored: Thu Feb 2 23:36:08 2017 -0800
Committer: maryannxue <ma...@gmail.com>
Committed: Fri Feb 3 21:59:55 2017 -0800

----------------------------------------------------------------------
 .../apache/phoenix/calcite/CalciteDMLIT.java    |   6 +-
 .../phoenix/calcite/CalciteGlobalIndexIT.java   |  10 +-
 .../org/apache/phoenix/calcite/CalciteIT.java   |  78 ++--
 .../phoenix/calcite/CalciteLocalIndexIT.java    |  14 +-
 .../phoenix/calcite/PhoenixPrepareImpl.java     |  61 +--
 .../apache/phoenix/calcite/PhoenixPrograms.java | 370 +++++++++++++++++++
 .../apache/phoenix/calcite/PhoenixTable.java    |  12 +-
 .../calcite/metadata/PhoenixRelMdCollation.java |  85 ++++-
 .../metadata/PhoenixRelMdMaxRowCount.java       |  32 ++
 .../calcite/metadata/PhoenixRelMdRowCount.java  |   4 +-
 .../calcite/metadata/PhoenixRelMdSize.java      |  49 ++-
 .../metadata/PhoenixRelMetadataProvider.java    |   3 +-
 .../org/apache/phoenix/calcite/rel/Limit.java   |  40 ++
 .../phoenix/calcite/rel/LogicalLimit.java       |  45 +++
 .../calcite/rel/PhoenixClientProject.java       |   4 +-
 .../phoenix/calcite/rel/PhoenixLimit.java       |  25 +-
 .../calcite/rel/PhoenixServerProject.java       |   4 +-
 .../phoenix/calcite/rel/PhoenixTableScan.java   |  21 ++
 .../calcite/rel/PhoenixTemporarySort.java       |   2 +-
 .../calcite/rules/PhoenixConverterRules.java    |  26 +-
 .../rules/PhoenixForwardTableScanRule.java      |  13 +-
 ...hoenixJoinSingleValueAggregateMergeRule.java |  11 +-
 .../rules/PhoenixOrderedAggregateRule.java      |   9 +-
 .../rules/PhoenixReverseTableScanRule.java      |   9 +-
 .../PhoenixSortServerJoinTransposeRule.java     |  18 +-
 .../rules/ProjectLimitTransposeRule.java        |  37 ++
 .../calcite/rules/ProjectSortTransposeRule.java |  70 ++++
 .../phoenix/calcite/rules/SortSplitRule.java    |  40 ++
 28 files changed, 902 insertions(+), 196 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDMLIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDMLIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDMLIT.java
index 6417ad2..116fa50 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDMLIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteDMLIT.java
@@ -36,7 +36,7 @@ public class CalciteDMLIT extends BaseCalciteIT {
     public void testUpsertValues() throws Exception {
         start(PROPS).sql("upsert into atable(organization_id, entity_id) values('1', '1')")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[INSERT], updateColumnList=[[]], flattened=[false])\n" +
+                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[INSERT], flattened=[false])\n" +
                        "    PhoenixClientProject(ORGANIZATION_ID=[$0], ENTITY_ID=[$1], A_STRING=[null], B_STRING=[null], A_INTEGER=[null], A_DATE=[null], A_TIME=[null], A_TIMESTAMP=[null], X_DECIMAL=[null], X_LONG=[null], X_INTEGER=[null], Y_INTEGER=[null], A_BYTE=[null], A_SHORT=[null], A_FLOAT=[null], A_DOUBLE=[null], A_UNSIGNED_FLOAT=[null], A_UNSIGNED_DOUBLE=[null])\n" +
                        "      PhoenixValues(tuples=[[{ '1              ', '1              ' }]])\n")
             .executeUpdate()
@@ -106,7 +106,7 @@ public class CalciteDMLIT extends BaseCalciteIT {
     @Test public void testDelete() throws Exception {
         start(PROPS).sql("upsert into atable(organization_id, entity_id) values('1', '1')")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[INSERT], updateColumnList=[[]], flattened=[false])\n" +
+                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[INSERT], flattened=[false])\n" +
                        "    PhoenixClientProject(ORGANIZATION_ID=[$0], ENTITY_ID=[$1], A_STRING=[null], B_STRING=[null], A_INTEGER=[null], A_DATE=[null], A_TIME=[null], A_TIMESTAMP=[null], X_DECIMAL=[null], X_LONG=[null], X_INTEGER=[null], Y_INTEGER=[null], A_BYTE=[null], A_SHORT=[null], A_FLOAT=[null], A_DOUBLE=[null], A_UNSIGNED_FLOAT=[null], A_UNSIGNED_DOUBLE=[null])\n" +
                        "      PhoenixValues(tuples=[[{ '1              ', '1              ' }]])\n")
             .executeUpdate()
@@ -116,7 +116,7 @@ public class CalciteDMLIT extends BaseCalciteIT {
             .close();
         start(PROPS).sql("delete from atable where organization_id = '1' and entity_id = '1'")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[DELETE], updateColumnList=[[]], flattened=[false])\n" +
+                       "  PhoenixTableModify(table=[[phoenix, ATABLE]], operation=[DELETE], flattened=[false])\n" +
                        "    PhoenixTableScan(table=[[phoenix, ATABLE]], filter=[AND(=($0, CAST('1'):CHAR(15) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL), =($1, CAST('1'):CHAR(15) CHARACTER SET \"ISO-8859-1\" COLLATE \"ISO-8859-1$en_US$primary\" NOT NULL))])\n")
             .executeUpdate()
             .close();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
index 090f47f..d5a95ff 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteGlobalIndexIT.java
@@ -38,12 +38,12 @@ public class CalciteGlobalIndexIT extends BaseCalciteIndexIT {
             .close();
         start(true, 1000f).sql("select x_integer from aTable")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(X_INTEGER=[$4])\n" +
+                       "  PhoenixServerProject(0:X_INTEGER=[$4])\n" +
                        "    PhoenixTableScan(table=[[phoenix, IDX1]])\n")
             .close();
         start(true, 1000f).sql("select a_string from aTable order by a_string")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$0])\n" +
+                       "  PhoenixServerProject(0:A_STRING=[$0])\n" +
                        "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n")
             .close();
         start(true, 1000000f).sql("select a_string from aTable order by organization_id")
@@ -59,7 +59,7 @@ public class CalciteGlobalIndexIT extends BaseCalciteIndexIT {
             .close();
         start(true, 1000f).sql("select a_string, b_string from aTable where a_string = 'a'")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$0], B_STRING=[$3])\n" +
+                       "  PhoenixServerProject(0:A_STRING=[$0], 0:B_STRING=[$3])\n" +
                        "    PhoenixTableScan(table=[[phoenix, IDX1]], filter=[=($0, 'a')])\n")
             .close();
         start(true, 1000f).sql("select a_string, b_string from aTable where b_string = 'b'")
@@ -129,9 +129,9 @@ public class CalciteGlobalIndexIT extends BaseCalciteIndexIT {
                         {1, 2, 3, 4},
                         {2, 3, 4, 5}})
                 .close();
-        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk0 = s2.mypk0 and s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
+        start(true, 1f).sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerJoin(condition=[AND(=($0, $4), =($1, $5))], joinType=[inner])\n" +
+                           "  PhoenixServerJoin(condition=[=($1, $5)], joinType=[inner])\n" +
                            "    PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n" +
                            "    PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
                            "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIT.java
index a649c7e..c26e0b5 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteIT.java
@@ -282,13 +282,13 @@ public class CalciteIT extends BaseCalciteIT {
                 .close();
 
         start(false, 1000f).sql("SELECT \"order_id\", i.name, i.price, discount2, quantity FROM " + JOIN_ORDER_TABLE_FULL_NAME + " o LEFT JOIN "
-                + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" limit 2 offset 1")
+                + JOIN_ITEM_TABLE_FULL_NAME + " i ON o.\"item_id\" = i.\"item_id\" limit 2")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(order_id=[$0], NAME=[$4], PRICE=[$5], DISCOUNT2=[$6], QUANTITY=[$2])\n" +
-                           "    PhoenixLimit(offset=[1], fetch=[2])\n" +
+                           "  PhoenixLimit(fetch=[2])\n" +
+                           "    PhoenixClientProject(order_id=[$0], NAME=[$4], PRICE=[$5], DISCOUNT2=[$6], QUANTITY=[$2])\n" +
                            "      PhoenixClientJoin(condition=[=($1, $3)], joinType=[left])\n" +
                            "        PhoenixClientSort(sort0=[$1], dir0=[ASC])\n" +
-                           "          PhoenixLimit(offset=[1], fetch=[2])\n" +
+                           "          PhoenixLimit(fetch=[2])\n" +
                            "            PhoenixServerProject(order_id=[$0], item_id=[$2], QUANTITY=[$4])\n" +
                            "              PhoenixTableScan(table=[[phoenix, Join, OrderTable]])\n" +
                            "        PhoenixServerProject(item_id=[$0], NAME=[$1], PRICE=[$2], DISCOUNT2=[$4])\n" +
@@ -318,8 +318,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("select t1.entity_id, t2.a_string, t3.organization_id from aTable t1 join aTable t2 on t1.entity_id = t2.entity_id and t1.organization_id = t2.organization_id join atable t3 on t1.entity_id = t3.entity_id and t1.organization_id = t3.organization_id limit 8 offset 1")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(ENTITY_ID=[$1], A_STRING=[$6], ORGANIZATION_ID=[$2])\n" +
-                           "    PhoenixLimit(offset=[1], fetch=[8])\n" +
+                           "  PhoenixLimit(offset=[1], fetch=[8])\n" +
+                           "    PhoenixClientProject(ENTITY_ID=[$1], A_STRING=[$6], ORGANIZATION_ID=[$2])\n" +
                            "      PhoenixClientJoin(condition=[AND(=($1, $5), =($0, $4))], joinType=[inner])\n" +
                            "        PhoenixClientJoin(condition=[AND(=($1, $3), =($0, $2))], joinType=[inner])\n" +
                            "          PhoenixServerSort(sort0=[$1], sort1=[$0], dir0=[ASC], dir1=[ASC])\n" +
@@ -679,8 +679,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("select count(entity_id), a_string from atable group by a_string order by count(entity_id), a_string desc limit 2 offset 1")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
-                           "    PhoenixLimit(offset=[1], fetch=[2])\n" +
+                           "  PhoenixLimit(offset=[1], fetch=[2])\n" +
+                           "    PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
                            "      PhoenixServerSort(sort0=[$1], sort1=[$0], dir0=[ASC], dir1=[DESC])\n" +
                            "        PhoenixServerAggregate(group=[{2}], EXPR$0=[COUNT()], isOrdered=[false])\n" +
                            "          PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
@@ -707,8 +707,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " item JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" order by item.name desc limit 3")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
-                           "    PhoenixLimit(fetch=[3])\n" +
+                           "  PhoenixLimit(fetch=[3])\n" +
+                           "    PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
                            "      PhoenixServerSort(sort0=[$1], dir0=[DESC])\n" +
                            "        PhoenixServerJoin(condition=[=($2, $3)], joinType=[inner])\n" +
                            "          PhoenixServerProject(item_id=[$0], NAME=[$1], supplier_id=[$5])\n" +
@@ -742,8 +742,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("select count(entity_id), a_string from atable group by a_string limit 2")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
-                           "    PhoenixLimit(fetch=[2])\n" +
+                           "  PhoenixLimit(fetch=[2])\n" +
+                           "    PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
                            "      PhoenixServerAggregate(group=[{2}], EXPR$0=[COUNT()], isOrdered=[false])\n" +
                            "        PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
                 .resultIsSomeOf(2, new Object[][] {
@@ -770,8 +770,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " item JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" limit 3")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
-                           "    PhoenixLimit(fetch=[3])\n" +
+                           "  PhoenixLimit(fetch=[3])\n" +
+                           "    PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
                            "      PhoenixServerJoin(condition=[=($2, $3)], joinType=[inner])\n" +
                            "        PhoenixServerProject(item_id=[$0], NAME=[$1], supplier_id=[$5])\n" +
                            "          PhoenixTableScan(table=[[phoenix, Join, ItemTable]])\n" +
@@ -788,8 +788,8 @@ public class CalciteIT extends BaseCalciteIT {
         
         start(false, 1000f).sql("SELECT x from (values (1, 2), (2, 4), (3, 6)) as t(x, y) limit 2")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(X=[$0])\n" +
-                           "    PhoenixLimit(fetch=[2])\n" +
+                           "  PhoenixLimit(fetch=[2])\n" +
+                           "    PhoenixClientProject(X=[$0])\n" +
                            "      PhoenixValues(tuples=[[{ 1, 2 }, { 2, 4 }, { 3, 6 }]])\n")
                 .resultIsSomeOf(2, new Object[][] {{1}, {2}, {3}})
                 .close();
@@ -822,8 +822,8 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("select count(entity_id), a_string from atable group by a_string limit 2 offset 0")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
-                        "    PhoenixLimit(fetch=[2])\n" +
+                        "  PhoenixLimit(fetch=[2])\n" +
+                        "    PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
                         "      PhoenixServerAggregate(group=[{2}], EXPR$0=[COUNT()], isOrdered=[false])\n" +
                         "        PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
                 .resultIsSomeOf(2, new Object[][] {
@@ -834,8 +834,8 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " item JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" limit 3 offset 3")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
-                        "    PhoenixLimit(offset=[3], fetch=[3])\n" +
+                        "  PhoenixLimit(offset=[3], fetch=[3])\n" +
+                        "    PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
                         "      PhoenixServerJoin(condition=[=($2, $3)], joinType=[inner])\n" +
                         "        PhoenixServerProject(item_id=[$0], NAME=[$1], supplier_id=[$5])\n" +
                         "          PhoenixTableScan(table=[[phoenix, Join, ItemTable]])\n" +
@@ -852,16 +852,16 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("SELECT x from (values (1, 2), (2, 4), (3, 6)) as t(x, y) limit 2 offset 2")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(X=[$0])\n" +
-                        "    PhoenixLimit(offset=[2], fetch=[2])\n" +
+                        "  PhoenixLimit(offset=[2], fetch=[2])\n" +
+                        "    PhoenixClientProject(X=[$0])\n" +
                         "      PhoenixValues(tuples=[[{ 1, 2 }, { 2, 4 }, { 3, 6 }]])\n")
                 .resultIs(new Object[][] {{3}})
                 .close();
 
         start(false, 1000f).sql("SELECT x from (values (1, 2), (2, 4), (3, 6)) as t(x, y) limit 3 offset 4")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(X=[$0])\n" +
-                        "    PhoenixLimit(offset=[4], fetch=[3])\n" +
+                        "  PhoenixLimit(offset=[4], fetch=[3])\n" +
+                        "    PhoenixClientProject(X=[$0])\n" +
                         "      PhoenixValues(tuples=[[{ 1, 2 }, { 2, 4 }, { 3, 6 }]])\n")
                 .resultIs(new Object[][] {})
                 .close();
@@ -931,8 +931,8 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("select count(entity_id), a_string from atable group by a_string offset 1")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
-                        "    PhoenixLimit(offset=[1])\n" +
+                        "  PhoenixLimit(offset=[1])\n" +
+                        "    PhoenixClientProject(EXPR$0=[$1], A_STRING=[$0])\n" +
                         "      PhoenixServerAggregate(group=[{2}], EXPR$0=[COUNT()], isOrdered=[false])\n" +
                         "        PhoenixTableScan(table=[[phoenix, ATABLE]])\n")
                 .resultIsSomeOf(2, new Object[][] {
@@ -943,8 +943,8 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("SELECT item.\"item_id\", item.name, supp.\"supplier_id\", supp.name FROM " + JOIN_ITEM_TABLE_FULL_NAME + " item JOIN " + JOIN_SUPPLIER_TABLE_FULL_NAME + " supp ON item.\"supplier_id\" = supp.\"supplier_id\" offset 7")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
-                        "    PhoenixLimit(offset=[7])\n" +
+                        "  PhoenixLimit(offset=[7])\n" +
+                        "    PhoenixClientProject(item_id=[$0], NAME=[$1], supplier_id=[$3], NAME0=[$4])\n" +
                         "      PhoenixServerJoin(condition=[=($2, $3)], joinType=[inner])\n" +
                         "        PhoenixServerProject(item_id=[$0], NAME=[$1], supplier_id=[$5])\n" +
                         "          PhoenixTableScan(table=[[phoenix, Join, ItemTable]])\n" +
@@ -962,8 +962,8 @@ public class CalciteIT extends BaseCalciteIT {
 
         start(false, 1000f).sql("SELECT x from (values (1, 2), (2, 4), (3, 6)) as t(x, y) offset 3")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                        "  PhoenixClientProject(X=[$0])\n" +
-                        "    PhoenixLimit(offset=[3])\n" +
+                        "  PhoenixLimit(offset=[3])\n" +
+                        "    PhoenixClientProject(X=[$0])\n" +
                         "      PhoenixValues(tuples=[[{ 1, 2 }, { 2, 4 }, { 3, 6 }]])\n")
                 .resultIs(new Object[][] {})
                 .close();
@@ -1213,14 +1213,15 @@ public class CalciteIT extends BaseCalciteIT {
                 "          PhoenixTableScan(table=[[phoenix, Join, OrderTable]], filter=[=($cor0.item_id, $2)])\n";
         String p3Decorrelated = 
                 "PhoenixToEnumerableConverter\n" +
-                "  PhoenixClientSemiJoin(condition=[=($0, $2)], joinType=[inner])\n" +
+                "  PhoenixClientSemiJoin(condition=[=($0, $3)], joinType=[inner])\n" +
                 "    PhoenixServerProject(item_id=[$0], NAME=[$1])\n" +
                 "      PhoenixTableScan(table=[[phoenix, Join, ItemTable]], scanOrder=[FORWARD])\n" +
-                "    PhoenixServerJoin(condition=[=($0, $1)], joinType=[inner])\n" +
-                "      PhoenixServerProject(item_id=[$0])\n" +
-                "        PhoenixTableScan(table=[[phoenix, Join, ItemTable]], scanOrder=[FORWARD])\n" +
-                "      PhoenixServerProject(item_id=[$2])\n" +
-                "        PhoenixTableScan(table=[[phoenix, Join, OrderTable]])\n";
+                "    PhoenixClientProject(item_id=[$1], item_id0=[$0])\n" +
+                "      PhoenixServerJoin(condition=[=($0, $1)], joinType=[inner])\n" +
+                "        PhoenixServerProject(item_id=[$0])\n" +
+                "          PhoenixTableScan(table=[[phoenix, Join, ItemTable]], scanOrder=[FORWARD])\n" +
+                "        PhoenixServerProject(item_id=[$2])\n" +
+                "          PhoenixTableScan(table=[[phoenix, Join, OrderTable]])\n";
         start(correlProps).sql(q3).explainIs(p3Correlate).resultIs(0, r3).close();
         start(decorrelProps).sql(q3).explainIs(p3Decorrelated).resultIs(0, r3).close();
         
@@ -1232,11 +1233,10 @@ public class CalciteIT extends BaseCalciteIT {
                 {"0000000006", "T6"}};
         String p4Decorrelated = 
                 "PhoenixToEnumerableConverter\n" +
-                "  PhoenixServerSemiJoin(condition=[=($0, $2)], joinType=[inner])\n" +
+                "  PhoenixServerSemiJoin(condition=[=($0, $4)], joinType=[inner])\n" +
                 "    PhoenixServerProject(item_id=[$0], NAME=[$1])\n" +
                 "      PhoenixTableScan(table=[[phoenix, Join, ItemTable]])\n" +
-                "    PhoenixServerProject(item_id=[$2])\n" +
-                "      PhoenixTableScan(table=[[phoenix, Join, OrderTable]])\n";
+                "    PhoenixTableScan(table=[[phoenix, Join, OrderTable]])\n";
         start(decorrelProps).sql(q4).explainIs(p4Decorrelated).resultIs(0, r4).close();
         
         String q5 = "select \"order_id\" from " + JOIN_ITEM_TABLE_FULL_NAME + " i JOIN " + JOIN_ORDER_TABLE_FULL_NAME + " o on o.\"item_id\" = i.\"item_id\" where quantity = (select max(quantity) from " + JOIN_ORDER_TABLE_FULL_NAME + " o2 JOIN " + JOIN_ITEM_TABLE_FULL_NAME + " i2 on o2.\"item_id\" = i2.\"item_id\" where i.\"supplier_id\" = i2.\"supplier_id\")";

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
index 0c03715..15452b7 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/calcite/CalciteLocalIndexIT.java
@@ -47,7 +47,7 @@ public class CalciteLocalIndexIT extends BaseCalciteIndexIT {
                        "    PhoenixTableScan(table=[[phoenix, IDX1]])\n")*/
         start1000.sql("select a_string from aTable order by a_string")
             .explainIs("PhoenixToEnumerableConverter\n" +
-                       "  PhoenixServerProject(A_STRING=[$0])\n" +
+                       "  PhoenixServerProject(0:A_STRING=[$0])\n" +
                        "    PhoenixTableScan(table=[[phoenix, IDX1]], scanOrder=[FORWARD])\n");
         start1000000.sql("select a_string from aTable order by organization_id")
             .explainIs("PhoenixToEnumerableConverter\n" +
@@ -129,18 +129,12 @@ public class CalciteLocalIndexIT extends BaseCalciteIndexIT {
                 .resultIs(new Object[][] {
                         {1, 2, 3, 4},
                         {2, 3, 4, 5}});
-        start1.sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk0 = s2.mypk0 and s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
+        start1.sql("select * from " + SALTED_TABLE_NAME + " s1, " + SALTED_TABLE_NAME + " s2 where s1.mypk1 = s2.mypk1 and s1.mypk0 > 500 and s2.col1 < 505")
                 .explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixClientProject(MYPK0=[$4], MYPK1=[$5], COL0=[$6], COL1=[$7], MYPK00=[$0], MYPK10=[$1], COL00=[$2], COL10=[$3])\n" +
-                           "    PhoenixServerJoin(condition=[AND(=($4, $0), =($5, $1))], joinType=[inner])\n" +
-                           "      PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
-                           "        PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n")
-                /*.explainIs("PhoenixToEnumerableConverter\n" +
-                           "  PhoenixServerJoin(condition=[AND(=($0, $4), =($1, $5))], joinType=[inner])\n" +
+                           "  PhoenixServerJoin(condition=[=($1, $5)], joinType=[inner])\n" +
                            "    PhoenixTableScan(table=[[phoenix, SALTED_TEST_TABLE]], filter=[>($0, 500)])\n" +
                            "    PhoenixServerProject(MYPK0=[$1], MYPK1=[$2], COL0=[$3], COL1=[CAST($0):INTEGER])\n" +
-                           "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")*/
+                           "      PhoenixTableScan(table=[[phoenix, IDXSALTED_SALTED_TEST_TABLE]], filter=[<(CAST($0):INTEGER, 505)])\n")
                 .resultIs(0, new Object[][] {
                         {501, 502, 503, 504, 501, 502, 503, 504}});
         start1.close();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrepareImpl.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrepareImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrepareImpl.java
index 9edc4de..11be55b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrepareImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrepareImpl.java
@@ -7,7 +7,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.calcite.adapter.enumerable.EnumerableRules;
 import org.apache.calcite.jdbc.CalcitePrepare;
 import org.apache.calcite.jdbc.CalciteSchema;
 import org.apache.calcite.linq4j.Queryable;
@@ -18,11 +17,6 @@ import org.apache.calcite.plan.RelOptRule;
 import org.apache.calcite.prepare.CalcitePrepareImpl;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.convert.ConverterRule;
-import org.apache.calcite.rel.logical.LogicalSort;
-import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule;
-import org.apache.calcite.rel.rules.JoinCommuteRule;
-import org.apache.calcite.rel.rules.SortProjectTransposeRule;
-import org.apache.calcite.rel.rules.SortUnionTransposeRule;
 import org.apache.calcite.rex.RexBuilder;
 import org.apache.calcite.runtime.Hook;
 import org.apache.calcite.runtime.Hook.Closeable;
@@ -41,7 +35,6 @@ import org.apache.calcite.sql.parser.SqlParser;
 import org.apache.calcite.sql.parser.SqlParserPos;
 import org.apache.calcite.sql.parser.SqlParserUtil;
 import org.apache.calcite.tools.Program;
-import org.apache.calcite.tools.Programs;
 import org.apache.calcite.util.Holder;
 import org.apache.calcite.util.NlsString;
 import org.apache.hadoop.hbase.util.Pair;
@@ -62,17 +55,7 @@ import org.apache.phoenix.calcite.parse.SqlUploadJarsNode;
 import org.apache.phoenix.calcite.parse.SqlUseSchema;
 import org.apache.phoenix.calcite.parser.PhoenixParserImpl;
 import org.apache.phoenix.calcite.rel.PhoenixRel;
-import org.apache.phoenix.calcite.rel.PhoenixServerProject;
-import org.apache.phoenix.calcite.rel.PhoenixTemporarySort;
 import org.apache.phoenix.calcite.rules.PhoenixConverterRules.PhoenixToEnumerableConverterRule;
-import org.apache.phoenix.calcite.rules.PhoenixFilterScanMergeRule;
-import org.apache.phoenix.calcite.rules.PhoenixForwardTableScanRule;
-import org.apache.phoenix.calcite.rules.PhoenixJoinSingleValueAggregateMergeRule;
-import org.apache.phoenix.calcite.rules.PhoenixMergeSortUnionRule;
-import org.apache.phoenix.calcite.rules.PhoenixOrderedAggregateRule;
-import org.apache.phoenix.calcite.rules.PhoenixReverseTableScanRule;
-import org.apache.phoenix.calcite.rules.PhoenixSortServerJoinTransposeRule;
-import org.apache.phoenix.calcite.rules.PhoenixTableScanColumnRefRule;
 import org.apache.phoenix.compile.BaseMutationPlan;
 import org.apache.phoenix.compile.CreateIndexCompiler;
 import org.apache.phoenix.compile.CreateSequenceCompiler;
@@ -162,17 +145,12 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
             org.apache.calcite.plan.Context externalContext,
             RelOptCostFactory costFactory) {
         RelOptPlanner planner = super.createPlanner(prepareContext, externalContext, costFactory);
-        
-        planner.removeRule(EnumerableRules.ENUMERABLE_SEMI_JOIN_RULE);
-        planner.removeRule(JoinCommuteRule.INSTANCE);
-        planner.removeRule(AggregateExpandDistinctAggregatesRule.INSTANCE);
-        planner.addRule(JoinCommuteRule.SWAP_OUTER);
-        planner.removeRule(SortUnionTransposeRule.INSTANCE);
-        planner.addRule(SortUnionTransposeRule.MATCH_NULL_FETCH);
-        planner.addRule(new SortProjectTransposeRule(
-                PhoenixTemporarySort.class,
-                PhoenixServerProject.class,
-                "PhoenixSortProjectTransposeRule"));
+        for (RelOptRule rule : PhoenixPrograms.EXCLUDED_VOLCANO_RULES) {
+            planner.removeRule(rule);
+        }
+        for (RelOptRule rule : ENUMERABLE_RULES) {
+            //planner.removeRule(rule);
+        }
 
         final PhoenixConnection pc =
                 getPhoenixConnection(prepareContext.getRootSchema().plus());
@@ -190,16 +168,9 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
         for (RelOptRule rule : this.defaultConverterRules) {
             planner.addRule(rule);
         }
-        planner.addRule(PhoenixFilterScanMergeRule.INSTANCE);
-        planner.addRule(PhoenixTableScanColumnRefRule.INSTANCE);
-        planner.addRule(PhoenixJoinSingleValueAggregateMergeRule.INSTANCE);
-        planner.addRule(PhoenixMergeSortUnionRule.INSTANCE);
-        planner.addRule(PhoenixOrderedAggregateRule.INSTANCE);
-        planner.addRule(PhoenixSortServerJoinTransposeRule.INSTANCE);
-        planner.addRule(new PhoenixForwardTableScanRule(LogicalSort.class));
-        planner.addRule(new PhoenixForwardTableScanRule(PhoenixTemporarySort.class));
-        planner.addRule(new PhoenixReverseTableScanRule(LogicalSort.class));
-        planner.addRule(new PhoenixReverseTableScanRule(PhoenixTemporarySort.class));
+        for (RelOptRule rule : PhoenixPrograms.ADDITIONAL_VOLCANO_RULES) {
+            planner.addRule(rule);
+        }
 
         return planner;
     }
@@ -209,7 +180,8 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
             Queryable<T> queryable) {
         List<Closeable> hooks = addHooks(
                 context.getRootSchema(),
-                context.config().materializationsEnabled());
+                context.config().materializationsEnabled(),
+                context.config().forceDecorrelate());
         try {
             return super.prepareQueryable(context, queryable);
         } finally {
@@ -226,7 +198,8 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
             long maxRowCount) {
         List<Closeable> hooks = addHooks(
                 context.getRootSchema(),
-                context.config().materializationsEnabled());
+                context.config().materializationsEnabled(),
+                context.config().forceDecorrelate());
         try {
             return super.prepareSql(context, query, elementType, maxRowCount);
         } finally {
@@ -236,7 +209,8 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
         }
     }
     
-    private List<Closeable> addHooks(final CalciteSchema rootSchema, boolean materializationEnabled) {
+    private List<Closeable> addHooks(final CalciteSchema rootSchema,
+            boolean materializationEnabled, final boolean forceDecorrelate) {
         final List<Closeable> hooks = Lists.newArrayList();
 
         hooks.add(Hook.PARSE_TREE.add(new Function<Object[], Object>() {
@@ -272,7 +246,10 @@ public class PhoenixPrepareImpl extends CalcitePrepareImpl {
         hooks.add(Hook.PROGRAM.add(new Function<Holder<Program>, Object>() {
             @Override
             public Object apply(Holder<Program> input) {
-                input.set(Programs.standard(PhoenixRel.METADATA_PROVIDER));
+                input.set(
+                        PhoenixPrograms.standard(
+                                PhoenixRel.METADATA_PROVIDER,
+                                forceDecorrelate));
                 return null;
             }
         }));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrograms.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrograms.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrograms.java
new file mode 100644
index 0000000..58aa3c4
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/PhoenixPrograms.java
@@ -0,0 +1,370 @@
+package org.apache.phoenix.calcite;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.calcite.adapter.enumerable.EnumerableRules;
+import org.apache.calcite.config.CalciteConnectionConfig;
+import org.apache.calcite.plan.RelOptCost;
+import org.apache.calcite.plan.RelOptLattice;
+import org.apache.calcite.plan.RelOptMaterialization;
+import org.apache.calcite.plan.RelOptMaterializations;
+import org.apache.calcite.plan.RelOptPlanner;
+import org.apache.calcite.plan.RelOptRule;
+import org.apache.calcite.plan.RelOptUtil;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.RelFactories;
+import org.apache.calcite.rel.logical.LogicalSort;
+import org.apache.calcite.rel.metadata.RelMetadataProvider;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rel.rules.AggregateExpandDistinctAggregatesRule;
+import org.apache.calcite.rel.rules.AggregateJoinTransposeRule;
+import org.apache.calcite.rel.rules.AggregateProjectMergeRule;
+import org.apache.calcite.rel.rules.FilterAggregateTransposeRule;
+import org.apache.calcite.rel.rules.FilterCorrelateRule;
+import org.apache.calcite.rel.rules.FilterJoinRule;
+import org.apache.calcite.rel.rules.FilterMergeRule;
+import org.apache.calcite.rel.rules.FilterProjectTransposeRule;
+import org.apache.calcite.rel.rules.JoinCommuteRule;
+import org.apache.calcite.rel.rules.JoinPushExpressionsRule;
+import org.apache.calcite.rel.rules.MaterializedViewFilterScanRule;
+import org.apache.calcite.rel.rules.ProjectMergeRule;
+import org.apache.calcite.rel.rules.ProjectRemoveRule;
+import org.apache.calcite.rel.rules.SemiJoinRule;
+import org.apache.calcite.rel.rules.SortJoinTransposeRule;
+import org.apache.calcite.rel.rules.SortProjectTransposeRule;
+import org.apache.calcite.rel.rules.SortUnionTransposeRule;
+import org.apache.calcite.rel.rules.SubQueryRemoveRule;
+import org.apache.calcite.rel.rules.UnionToDistinctRule;
+import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.sql2rel.RelDecorrelator;
+import org.apache.calcite.sql2rel.RelFieldTrimmer;
+import org.apache.calcite.tools.Program;
+import org.apache.calcite.tools.Programs;
+import org.apache.calcite.tools.RelBuilder;
+import org.apache.calcite.util.Pair;
+import org.apache.phoenix.calcite.rel.PhoenixServerProject;
+import org.apache.phoenix.calcite.rel.PhoenixServerSort;
+import org.apache.phoenix.calcite.rel.PhoenixTemporarySort;
+import org.apache.phoenix.calcite.rules.PhoenixFilterScanMergeRule;
+import org.apache.phoenix.calcite.rules.PhoenixForwardTableScanRule;
+import org.apache.phoenix.calcite.rules.PhoenixJoinSingleValueAggregateMergeRule;
+import org.apache.phoenix.calcite.rules.PhoenixMergeSortUnionRule;
+import org.apache.phoenix.calcite.rules.PhoenixOrderedAggregateRule;
+import org.apache.phoenix.calcite.rules.PhoenixReverseTableScanRule;
+import org.apache.phoenix.calcite.rules.PhoenixSortServerJoinTransposeRule;
+import org.apache.phoenix.calcite.rules.PhoenixTableScanColumnRefRule;
+import org.apache.phoenix.calcite.rules.ProjectLimitTransposeRule;
+import org.apache.phoenix.calcite.rules.ProjectSortTransposeRule;
+import org.apache.phoenix.calcite.rules.SortSplitRule;
+
+import com.google.common.collect.ImmutableList;
+
+public class PhoenixPrograms {
+
+    public static final RelOptRule[] EXCLUDED_VOLCANO_RULES =
+            new RelOptRule[] {
+                    AggregateExpandDistinctAggregatesRule.INSTANCE,
+                    AggregateJoinTransposeRule.INSTANCE,
+                    //AggregateProjectMergeRule.INSTANCE,
+                    FilterAggregateTransposeRule.INSTANCE,
+                    FilterCorrelateRule.INSTANCE,
+                    FilterJoinRule.FILTER_ON_JOIN,
+                    FilterJoinRule.JOIN,
+                    FilterMergeRule.INSTANCE,
+                    FilterProjectTransposeRule.INSTANCE,
+                    JoinCommuteRule.INSTANCE,
+                    JoinPushExpressionsRule.INSTANCE,
+                    ProjectRemoveRule.INSTANCE,
+                    SemiJoinRule.INSTANCE,
+                    SortJoinTransposeRule.INSTANCE,
+                    SortUnionTransposeRule.INSTANCE,
+                    UnionToDistinctRule.INSTANCE,
+                    MaterializedViewFilterScanRule.INSTANCE,
+                    EnumerableRules.ENUMERABLE_SEMI_JOIN_RULE,
+            };
+
+    public static final RelOptRule[] EXCLUDED_ENUMERABLE_RULES =
+            new RelOptRule[] {
+                    EnumerableRules.ENUMERABLE_JOIN_RULE,
+                    EnumerableRules.ENUMERABLE_MERGE_JOIN_RULE,
+                    EnumerableRules.ENUMERABLE_SEMI_JOIN_RULE,
+                    EnumerableRules.ENUMERABLE_CORRELATE_RULE,
+                    EnumerableRules.ENUMERABLE_PROJECT_RULE,
+                    EnumerableRules.ENUMERABLE_FILTER_RULE,
+                    EnumerableRules.ENUMERABLE_AGGREGATE_RULE,
+                    EnumerableRules.ENUMERABLE_SORT_RULE,
+                    EnumerableRules.ENUMERABLE_LIMIT_RULE,
+                    EnumerableRules.ENUMERABLE_UNION_RULE,
+                    EnumerableRules.ENUMERABLE_INTERSECT_RULE,
+                    EnumerableRules.ENUMERABLE_MINUS_RULE,
+                    EnumerableRules.ENUMERABLE_TABLE_MODIFICATION_RULE,
+                    EnumerableRules.ENUMERABLE_VALUES_RULE,
+                    EnumerableRules.ENUMERABLE_WINDOW_RULE,
+            };
+
+    public static final RelOptRule[] ADDITIONAL_VOLCANO_RULES =
+            new RelOptRule[] {
+                    JoinCommuteRule.SWAP_OUTER,
+                    new SortProjectTransposeRule(
+                            PhoenixTemporarySort.class,
+                            PhoenixServerProject.class,
+                            "PhoenixTemporarySortProjectTransposeRule"),
+                    new SortProjectTransposeRule(
+                            PhoenixServerSort.class,
+                            PhoenixServerProject.class,
+                            "PhoenixServerSortProjectTransposeRule"),
+                    PhoenixMergeSortUnionRule.INSTANCE,
+                    PhoenixOrderedAggregateRule.INSTANCE,
+                    new PhoenixSortServerJoinTransposeRule(
+                            PhoenixTemporarySort.class,
+                            "PhoenixTemporarySortServerJoinTransposeRule"),
+                    new PhoenixSortServerJoinTransposeRule(
+                            PhoenixServerSort.class,
+                            "PhoenixServerSortServerJoinTransposeRule"),
+                    new PhoenixForwardTableScanRule(PhoenixTemporarySort.class),
+                    new PhoenixReverseTableScanRule(PhoenixTemporarySort.class),
+                    new PhoenixForwardTableScanRule(PhoenixServerSort.class),
+                    new PhoenixReverseTableScanRule(PhoenixServerSort.class),
+            };
+
+    public static Program standard(
+            final RelMetadataProvider metadataProvider,
+            final boolean forceDecorrelate) {
+        final Program volcanoProgram = new Program() {
+            public RelNode run(RelOptPlanner planner, RelNode rel,
+                    RelTraitSet requiredOutputTraits,
+                    List<RelOptMaterialization> materializations,
+                    List<RelOptLattice> lattices) {
+                final List<Pair<RelNode, List<RelOptMaterialization>>> materializationUses;
+                final Set<RelOptMaterialization> applicableMaterializations;
+                final CalciteConnectionConfig config =
+                        planner.getContext().unwrap(CalciteConnectionConfig.class);
+                if (config != null && config.materializationsEnabled()) {
+                    // Transform rels using materialized views.
+                    materializationUses =
+                            RelOptMaterializations.useMaterializedViews(rel, materializations);
+
+                    // Add not used but potentially useful materialized views to the planner.
+                    applicableMaterializations = new HashSet<>(
+                            RelOptMaterializations.getApplicableMaterializations(
+                                    rel, materializations));
+                    for (Pair<RelNode, List<RelOptMaterialization>> use : materializationUses) {
+                        applicableMaterializations.removeAll(use.right);
+                    }
+                } else {
+                    materializationUses = Collections.emptyList();
+                    applicableMaterializations = Collections.emptySet();
+                }
+
+                final List<RelOptRule> rules = planner.getRules();
+                // Plan for the original root rel.
+                Pair<RelNode, RelOptCost> res = planFor(
+                        planner, rel, requiredOutputTraits,
+                        rules, applicableMaterializations);
+                RelNode bestRel = res.left;
+                RelOptCost bestCost = res.right;
+                // Plan for transformed rels using materialized views and update the
+                // best cost as needed.
+                for (Pair<RelNode, List<RelOptMaterialization>> m : materializationUses) {
+                    try {
+                        final RelNode rel2 = m.left;
+                        Hook.SUB.run(rel2);
+                        res = planFor(planner, rel2, requiredOutputTraits,
+                                rules, applicableMaterializations);
+                        if (res.right.isLt(bestCost)) {
+                            bestRel = res.left;
+                            bestCost = res.right;
+                        }
+                    } catch (RelOptPlanner.CannotPlanException e) {
+                        // Ignore
+                    }
+                }
+                return bestRel;
+            }
+
+            private Pair<RelNode, RelOptCost> planFor(
+                    RelOptPlanner planner, RelNode rel,
+                    RelTraitSet requiredOutputTraits,
+                    List<RelOptRule> rules,
+                    Collection<RelOptMaterialization> materializations) {
+                final Program preProgram = pre(
+                        metadataProvider, forceDecorrelate);
+                rel = preProgram.run(planner, rel, requiredOutputTraits,
+                        Collections.<RelOptMaterialization>emptyList(),
+                        Collections.<RelOptLattice>emptyList());
+                planner.clear();
+                for (RelOptRule rule : rules) {
+                    planner.addRule(rule);
+                }
+                for (RelOptMaterialization m : materializations) {
+                    planner.addMaterialization(m);
+                }
+
+                final RelNode rootRel2 =
+                        rel.getTraitSet().equals(requiredOutputTraits)
+                        ? rel
+                                : planner.changeTraits(rel, requiredOutputTraits);
+                assert rootRel2 != null;
+
+                planner.setRoot(rootRel2);
+                final RelOptPlanner planner2 = planner.chooseDelegate();
+                final RelNode rootRel3 = planner2.findBestExp();
+                final RelMetadataQuery mq = RelMetadataQuery.instance();
+                final RelOptCost cost = planner2.getCost(rootRel3, mq);
+
+                return Pair.of(rootRel3, cost);
+            }
+        };
+
+        return Programs.sequence(
+                volcanoProgram,
+                // Second planner pass to do physical "tweaks". This the first time that
+                // EnumerableCalcRel is introduced.
+                Programs.calc(metadataProvider));
+    }
+
+    public static Program pre(
+            RelMetadataProvider metadataProvider, boolean forceDecorrelate) {
+        return Programs.sequence(
+                subquery(metadataProvider),
+                decorrelate(forceDecorrelate),
+                trimFields(),
+                prePlanning(metadataProvider));        
+    }
+    
+    protected static Program subquery(RelMetadataProvider metadataProvider) {
+        return Programs.hep(
+                ImmutableList.of((RelOptRule) SubQueryRemoveRule.FILTER,
+                        SubQueryRemoveRule.PROJECT,
+                        SubQueryRemoveRule.JOIN), true, metadataProvider);
+    }
+
+    protected static Program decorrelate(final boolean forceDecorrelate) {
+        return new Program() {
+            public RelNode run(RelOptPlanner planner, RelNode rel,
+                    RelTraitSet requiredOutputTraits,
+                    List<RelOptMaterialization> materializations,
+                    List<RelOptLattice> lattices) {
+                if (forceDecorrelate) {
+                    return RelDecorrelator.decorrelateQuery(rel);
+                }
+                return rel;
+            }
+        };
+    }
+
+    protected static Program trimFields() {
+        return new Program() {
+            public RelNode run(RelOptPlanner planner, RelNode rel,
+                    RelTraitSet requiredOutputTraits,
+                    List<RelOptMaterialization> materializations,
+                    List<RelOptLattice> lattices) {
+                final RelBuilder relBuilder =
+                        RelFactories.LOGICAL_BUILDER.create(
+                                rel.getCluster(), null);
+                return new RelFieldTrimmer(null, relBuilder).trim(rel);
+            }
+        };
+    }
+
+    protected static Program prePlanning(RelMetadataProvider metadataProvider) {
+        final Program filterPushdown = repeat(
+                Programs.hep(
+                        ImmutableList.of(
+                                FilterCorrelateRule.INSTANCE,
+                                FilterJoinRule.FILTER_ON_JOIN,
+                                FilterJoinRule.JOIN,
+                                FilterProjectTransposeRule.INSTANCE,
+                                FilterAggregateTransposeRule.INSTANCE,
+                                FilterMergeRule.INSTANCE),
+                        true, metadataProvider));
+        final Program filterMerge = Programs.hep(
+                ImmutableList.of(PhoenixFilterScanMergeRule.INSTANCE),
+                true, metadataProvider);
+        final Program sortPushdown = Programs.sequence(
+                    repeat(
+                            Programs.hep(
+                                    ImmutableList.of(
+                                            SortProjectTransposeRule.INSTANCE,
+                                            SortJoinTransposeRule.INSTANCE,
+                                            SortUnionTransposeRule.MATCH_NULL_FETCH),
+                                    true, metadataProvider)),
+                    Programs.hep(
+                            ImmutableList.of(
+                                    SortSplitRule.INSTANCE),
+                            true, metadataProvider),
+                    // SortUnionTransposeRule does not work with offset != null, so
+                    // we should give it one more try after SortSplitRule.
+                    repeat(
+                            Programs.hep(
+                                    ImmutableList.of(
+                                            SortUnionTransposeRule.MATCH_NULL_FETCH,
+                                            SortProjectTransposeRule.INSTANCE),
+                                    true, metadataProvider)));
+        final Program sortMerge = Programs.hep(
+                ImmutableList.of(
+                        new PhoenixForwardTableScanRule(LogicalSort.class),
+                        new PhoenixReverseTableScanRule(LogicalSort.class)),
+                true, metadataProvider);
+        final Program projectPushdown = repeat(
+                Programs.hep(
+                        ImmutableList.of(
+                                ProjectLimitTransposeRule.INSTANCE,
+                                ProjectSortTransposeRule.INSTANCE),
+                        true, metadataProvider));
+        final Program projectMerge = Programs.hep(
+                ImmutableList.of(
+                        ProjectMergeRule.INSTANCE,
+                        PhoenixTableScanColumnRefRule.INSTANCE,
+                        ProjectRemoveRule.INSTANCE,
+                        AggregateProjectMergeRule.INSTANCE),
+                true, metadataProvider);
+        final Program misc1 = Programs.hep(
+                ImmutableList.of(
+                        ProjectRemoveRule.INSTANCE,
+                        PhoenixJoinSingleValueAggregateMergeRule.INSTANCE,
+                        AggregateJoinTransposeRule.INSTANCE,
+                        JoinPushExpressionsRule.INSTANCE,
+                        //PhoenixJoinCommuteRule.INSTANCE,
+                        UnionToDistinctRule.INSTANCE),
+                true, metadataProvider);
+        final Program misc2 = Programs.hep(
+                ImmutableList.of(
+                        SemiJoinRule.INSTANCE),
+                true, metadataProvider);
+        return Programs.sequence(
+                misc1,
+                filterPushdown,
+                filterMerge,
+                sortPushdown,
+                sortMerge,
+                projectPushdown,
+                projectMerge,
+                misc2);
+    }
+
+    private static Program repeat(final Program program) {
+        return new Program() {
+            @Override
+            public RelNode run(RelOptPlanner planner, RelNode rel,
+                    RelTraitSet requiredOutputTraits,
+                    List<RelOptMaterialization> materializations,
+                    List<RelOptLattice> lattices) {
+                RelNode oldRel = rel;
+                rel = program.run(planner, oldRel,
+                        requiredOutputTraits, materializations, lattices);
+                while (!RelOptUtil.toString(oldRel).equals(RelOptUtil.toString(rel))) {
+                    oldRel = rel;
+                    rel = program.run(planner, oldRel,
+                            requiredOutputTraits, materializations, lattices);
+                }
+                return rel;
+            }            
+        };
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/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 b29969f..f3d44eb 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
@@ -96,8 +96,16 @@ public class PhoenixTable extends AbstractTable
           StatementContext context = new StatementContext(stmt, resolver, new Scan(), new SequenceManager(stmt));
           Pair<Long, Long> estimatedCount = BaseResultIterators.getEstimatedCount(context, pTable);
           if (estimatedCount.getFirst() != null) {
-              rowCount = estimatedCount.getFirst();
-              byteCount = estimatedCount.getSecond();
+              // FIXME Right now the row count for local index is not correct.
+              if (dataTable == null) {
+                  rowCount = estimatedCount.getFirst();
+                  byteCount = estimatedCount.getSecond();
+              } else {
+                  Pair<Long, Long> dataTableEstimatedCount =
+                          BaseResultIterators.getEstimatedCount(context, dataTable.getTable());
+                  rowCount = dataTableEstimatedCount.getFirst();
+                  byteCount = dataTableEstimatedCount.getSecond();
+              }
           } else {
               // TODO The props might not be the same as server props.
               int guidepostPerRegion = pc.getQueryServices().getProps().getInt(

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdCollation.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdCollation.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdCollation.java
index 281ed2b..7e9207d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdCollation.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdCollation.java
@@ -1,11 +1,20 @@
 package org.apache.phoenix.calcite.metadata;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
+import org.apache.calcite.linq4j.Ord;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollations;
+import org.apache.calcite.rel.RelFieldCollation;
 import org.apache.calcite.rel.RelNode;
 import org.apache.calcite.rel.core.JoinRelType;
+import org.apache.calcite.rel.core.Project;
 import org.apache.calcite.rel.metadata.BuiltInMetadata;
 import org.apache.calcite.rel.metadata.MetadataDef;
 import org.apache.calcite.rel.metadata.MetadataHandler;
@@ -13,17 +22,24 @@ import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexCall;
+import org.apache.calcite.rex.RexCallBinding;
+import org.apache.calcite.rex.RexInputRef;
+import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SemiJoinType;
+import org.apache.calcite.sql.validate.SqlMonotonicity;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableIntList;
+import org.apache.phoenix.calcite.rel.Limit;
 import org.apache.phoenix.calcite.rel.PhoenixClientJoin;
 import org.apache.phoenix.calcite.rel.PhoenixCorrelate;
-import org.apache.phoenix.calcite.rel.PhoenixLimit;
 import org.apache.phoenix.calcite.rel.PhoenixMergeSortUnion;
 import org.apache.phoenix.calcite.rel.PhoenixServerJoin;
 import org.apache.phoenix.calcite.rel.PhoenixTableScan;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.LinkedListMultimap;
+import com.google.common.collect.Multimap;
 
 public class PhoenixRelMdCollation implements MetadataHandler<BuiltInMetadata.Collation> {
     public static final RelMetadataProvider SOURCE =
@@ -37,6 +53,10 @@ public class PhoenixRelMdCollation implements MetadataHandler<BuiltInMetadata.Co
         return BuiltInMetadata.Collation.DEF;
     }
 
+    public ImmutableList<RelCollation> collations(Project project, RelMetadataQuery mq) {
+        return ImmutableList.copyOf(project(mq, project.getInput(), project.getProjects()));
+    }
+
     public ImmutableList<RelCollation> collations(PhoenixTableScan tableScan, RelMetadataQuery mq) {
         return ImmutableList.copyOf(tableScan.getCollationList());
     }
@@ -45,7 +65,7 @@ public class PhoenixRelMdCollation implements MetadataHandler<BuiltInMetadata.Co
         return ImmutableList.copyOf(correlate(mq, correlate.getLeft(), correlate.getRight(), correlate.getJoinType()));
     }
 
-    public ImmutableList<RelCollation> collations(PhoenixLimit limit, RelMetadataQuery mq) {
+    public ImmutableList<RelCollation> collations(Limit limit, RelMetadataQuery mq) {
         return ImmutableList.copyOf(RelMdCollation.limit(mq, limit.getInput()));
     }
 
@@ -94,4 +114,65 @@ public class PhoenixRelMdCollation implements MetadataHandler<BuiltInMetadata.Co
         return builder.build();
     }
 
+    public static List<RelCollation> project(RelMetadataQuery mq,
+            RelNode input, List<? extends RexNode> projects) {
+        final SortedSet<RelCollation> collations = new TreeSet<>();
+        final List<RelCollation> inputCollations = mq.collations(input);
+        if (inputCollations == null || inputCollations.isEmpty()) {
+            return ImmutableList.of();
+        }
+        final Multimap<Integer, Integer> targets = LinkedListMultimap.create();
+        final Map<Integer, SqlMonotonicity> targetsWithMonotonicity =
+                new HashMap<>();
+        for (Ord<? extends RexNode> project : Ord.zip(projects)) {
+            if (project.e instanceof RexInputRef) {
+                targets.put(((RexInputRef) project.e).getIndex(), project.i);
+            } else if (project.e instanceof RexCall) {
+                final RexCall call = (RexCall) project.e;
+                final RexCallBinding binding =
+                        RexCallBinding.create(input.getCluster().getTypeFactory(), call, inputCollations);
+                targetsWithMonotonicity.put(project.i, call.getOperator().getMonotonicity(binding));
+            }
+        }
+        final List<RelFieldCollation> fieldCollations = new ArrayList<>();
+            for (RelCollation ic : inputCollations) {
+                if (ic.getFieldCollations().isEmpty()) {
+                    continue;
+                }
+                fieldCollations.clear();
+                for (RelFieldCollation ifc : ic.getFieldCollations()) {
+                    final Collection<Integer> integers = targets.get(ifc.getFieldIndex());
+                    if (integers.isEmpty()) {
+                        break;
+                    }
+                    fieldCollations.add(ifc.copy(integers.iterator().next()));
+                }
+                if (!fieldCollations.isEmpty()) {
+                    collations.add(RelCollations.of(fieldCollations));
+                }
+            }
+
+        final List<RelFieldCollation> fieldCollationsForRexCalls =
+                new ArrayList<>();
+        for (Map.Entry<Integer, SqlMonotonicity> entry
+                : targetsWithMonotonicity.entrySet()) {
+            final SqlMonotonicity value = entry.getValue();
+            switch (value) {
+            case NOT_MONOTONIC:
+            case CONSTANT:
+                break;
+            default:
+                fieldCollationsForRexCalls.add(
+                        new RelFieldCollation(entry.getKey(),
+                                RelFieldCollation.Direction.of(value)));
+                break;
+            }
+        }
+
+        if (!fieldCollationsForRexCalls.isEmpty()) {
+            collations.add(RelCollations.of(fieldCollationsForRexCalls));
+        }
+
+        return ImmutableList.copyOf(collations);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdMaxRowCount.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdMaxRowCount.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdMaxRowCount.java
new file mode 100644
index 0000000..a425f05
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdMaxRowCount.java
@@ -0,0 +1,32 @@
+package org.apache.phoenix.calcite.metadata;
+
+import org.apache.calcite.plan.hep.HepRelVertex;
+import org.apache.calcite.rel.metadata.BuiltInMetadata;
+import org.apache.calcite.rel.metadata.MetadataDef;
+import org.apache.calcite.rel.metadata.MetadataHandler;
+import org.apache.calcite.rel.metadata.ReflectiveRelMetadataProvider;
+import org.apache.calcite.rel.metadata.RelMetadataProvider;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.util.BuiltInMethod;
+import org.apache.phoenix.calcite.rel.Limit;
+
+public class PhoenixRelMdMaxRowCount implements MetadataHandler<BuiltInMetadata.MaxRowCount> {
+    public static final RelMetadataProvider SOURCE =
+            ReflectiveRelMetadataProvider.reflectiveSource(
+                BuiltInMethod.MAX_ROW_COUNT.method, new PhoenixRelMdMaxRowCount());
+    
+    private PhoenixRelMdMaxRowCount() { }
+    
+    @Override
+    public MetadataDef<BuiltInMetadata.MaxRowCount> getDef() {
+        return BuiltInMetadata.MaxRowCount.DEF;
+    }
+    
+    public Double getMaxRowCount(HepRelVertex rel, RelMetadataQuery mq) {
+        return mq.getMaxRowCount(rel.getCurrentRel());
+    }
+    
+    public Double getMaxRowCount(Limit rel, RelMetadataQuery mq) {
+        return rel.estimateRowCount(mq);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdRowCount.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdRowCount.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdRowCount.java
index aab8624..da34f96 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdRowCount.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdRowCount.java
@@ -9,8 +9,8 @@ import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.calcite.util.ImmutableBitSet;
+import org.apache.phoenix.calcite.rel.Limit;
 import org.apache.phoenix.calcite.rel.PhoenixAbstractAggregate;
-import org.apache.phoenix.calcite.rel.PhoenixLimit;
 
 public class PhoenixRelMdRowCount implements MetadataHandler<BuiltInMetadata.RowCount> {
     public static final RelMetadataProvider SOURCE =
@@ -43,7 +43,7 @@ public class PhoenixRelMdRowCount implements MetadataHandler<BuiltInMetadata.Row
         }
     }
     
-    public Double getRowCount(PhoenixLimit rel, RelMetadataQuery mq) {
+    public Double getRowCount(Limit rel, RelMetadataQuery mq) {
         return rel.estimateRowCount(mq);
     }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdSize.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdSize.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdSize.java
index 355de27..ee37195 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdSize.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMdSize.java
@@ -2,6 +2,15 @@ package org.apache.phoenix.calcite.metadata;
 
 import org.apache.calcite.plan.volcano.RelSubset;
 import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.core.Aggregate;
+import org.apache.calcite.rel.core.Correlate;
+import org.apache.calcite.rel.core.Filter;
+import org.apache.calcite.rel.core.Join;
+import org.apache.calcite.rel.core.Project;
+import org.apache.calcite.rel.core.SemiJoin;
+import org.apache.calcite.rel.core.Sort;
+import org.apache.calcite.rel.core.Uncollect;
+import org.apache.calcite.rel.core.Union;
 import org.apache.calcite.rel.metadata.BuiltInMetadata;
 import org.apache.calcite.rel.metadata.MetadataDef;
 import org.apache.calcite.rel.metadata.MetadataHandler;
@@ -10,11 +19,8 @@ import org.apache.calcite.rel.metadata.RelMetadataProvider;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.util.BuiltInMethod;
 import org.apache.phoenix.calcite.PhoenixTable;
-import org.apache.phoenix.calcite.rel.PhoenixAbstractAggregate;
-import org.apache.phoenix.calcite.rel.PhoenixAbstractProject;
-import org.apache.phoenix.calcite.rel.PhoenixAbstractSemiJoin;
+import org.apache.phoenix.calcite.rel.Limit;
 import org.apache.phoenix.calcite.rel.PhoenixTableScan;
-import org.apache.phoenix.calcite.rel.PhoenixUnion;
 
 public class PhoenixRelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
     /** Source for
@@ -30,7 +36,7 @@ public class PhoenixRelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
         return BuiltInMetadata.Size.DEF;
     }
     
-    public Double averageRowSize(PhoenixUnion rel, RelMetadataQuery mq) {
+    public Double averageRowSize(Union rel, RelMetadataQuery mq) {
         double rowSize = 0;
         for (RelNode input : rel.getInputs()) {
             rowSize += mq.getAverageRowSize(input);
@@ -39,7 +45,7 @@ public class PhoenixRelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
         return rowSize / rel.getInputs().size();
     }
     
-    public Double averageRowSize(PhoenixAbstractAggregate rel, RelMetadataQuery mq) {
+    public Double averageRowSize(Aggregate rel, RelMetadataQuery mq) {
         RelNode input = rel.getInput();
         double rowSize = mq.getAverageRowSize(input);
         rowSize = rowSize * (rel.getGroupCount() + rel.getAggCallList().size()) / input.getRowType().getFieldCount();
@@ -47,18 +53,43 @@ public class PhoenixRelMdSize implements MetadataHandler<BuiltInMetadata.Size> {
         return rowSize;
     }
     
-    public Double averageRowSize(PhoenixAbstractProject rel, RelMetadataQuery mq) {
+    public Double averageRowSize(Project rel, RelMetadataQuery mq) {
         RelNode input = rel.getInput();
         double rowSize = mq.getAverageRowSize(input);
         rowSize = rowSize * rel.getProjects().size() / input.getRowType().getFieldCount();
         
         return rowSize;
     }
-    
-    public Double averageRowSize(PhoenixAbstractSemiJoin rel, RelMetadataQuery mq) {
+
+    public Double averageRowSize(Filter rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getInput());
+    }
+
+    public Double averageRowSize(Correlate rel, RelMetadataQuery mq) {
         return mq.getAverageRowSize(rel.getLeft());
     }
+
+    public Double averageRowSize(SemiJoin rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getLeft());
+    }
+
+    public Double averageRowSize(Join rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getLeft())
+                + mq.getAverageRowSize(rel.getRight());
+    }
+
+    public Double averageRowSize(Sort rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getInput());
+    }
     
+    public Double averageRowSize(Limit rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getInput());
+    }
+
+    public Double averageRowSize(Uncollect rel, RelMetadataQuery mq) {
+        return mq.getAverageRowSize(rel.getInput());
+    }
+
     public Double averageRowSize(PhoenixTableScan rel, RelMetadataQuery mq) {
         PhoenixTable phoenixTable = rel.getTable().unwrap(PhoenixTable.class);
         return 1.0 * phoenixTable.byteCount / phoenixTable.rowCount;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMetadataProvider.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMetadataProvider.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMetadataProvider.java
index 2bbc237..1234a2e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMetadataProvider.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/metadata/PhoenixRelMetadataProvider.java
@@ -8,7 +8,8 @@ public class PhoenixRelMetadataProvider extends ChainedRelMetadataProvider {
 
     public PhoenixRelMetadataProvider() {
         super(ImmutableList.of(
-                PhoenixRelMdRowCount.SOURCE, 
+                PhoenixRelMdRowCount.SOURCE,
+                PhoenixRelMdMaxRowCount.SOURCE,
                 PhoenixRelMdCollation.SOURCE,
                 PhoenixRelMdSize.SOURCE,
                 DefaultRelMetadataProvider.INSTANCE));

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/Limit.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/Limit.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/Limit.java
new file mode 100644
index 0000000..95aa18a
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/Limit.java
@@ -0,0 +1,40 @@
+package org.apache.phoenix.calcite.rel;
+
+import java.util.List;
+
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.RelWriter;
+import org.apache.calcite.rel.SingleRel;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexLiteral;
+import org.apache.calcite.rex.RexNode;
+
+public abstract class Limit extends SingleRel {
+    public final RexNode offset;
+    public final RexNode fetch;
+
+    protected Limit(RelOptCluster cluster, RelTraitSet traits, RelNode input, RexNode offset, RexNode fetch) {
+        super(cluster, traits, input);
+        this.offset = offset;
+        this.fetch = fetch;
+    }
+
+    public abstract Limit copy(RelTraitSet traitSet, List<RelNode> newInputs);
+
+    @Override 
+    public RelWriter explainTerms(RelWriter pw) {
+        return super.explainTerms(pw)
+                .itemIf("offset", offset, offset != null)
+                .itemIf("fetch", fetch, fetch != null);
+    }
+    
+    @Override 
+    public double estimateRowCount(RelMetadataQuery mq) {
+        double rows = super.estimateRowCount(mq);
+        int offset = this.offset == null ? 0 : RexLiteral.intValue(this.offset);
+        int fetch = this.fetch == null ? Integer.MAX_VALUE : RexLiteral.intValue(this.fetch);
+        return Math.max(0, Math.min(fetch, rows - offset));
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/LogicalLimit.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/LogicalLimit.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/LogicalLimit.java
new file mode 100644
index 0000000..5fe7b66
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/LogicalLimit.java
@@ -0,0 +1,45 @@
+package org.apache.phoenix.calcite.rel;
+
+import java.util.List;
+
+import org.apache.calcite.plan.Convention;
+import org.apache.calcite.plan.RelOptCluster;
+import org.apache.calcite.plan.RelTraitSet;
+import org.apache.calcite.rel.RelCollation;
+import org.apache.calcite.rel.RelCollationTraitDef;
+import org.apache.calcite.rel.RelNode;
+import org.apache.calcite.rel.metadata.RelMdCollation;
+import org.apache.calcite.rel.metadata.RelMetadataQuery;
+import org.apache.calcite.rex.RexNode;
+import com.google.common.base.Supplier;
+
+public class LogicalLimit extends Limit {
+    
+    public static LogicalLimit create(final RelNode input, RexNode offset, RexNode fetch) {
+        final RelOptCluster cluster = input.getCluster();
+        final RelMetadataQuery mq = RelMetadataQuery.instance();
+        final RelTraitSet traits =
+                cluster.traitSet().replace(Convention.NONE)
+                .replaceIfs(RelCollationTraitDef.INSTANCE,
+                        new Supplier<List<RelCollation>>() {
+                    public List<RelCollation> get() {
+                        return RelMdCollation.limit(mq, input);
+                    }
+                });
+        return new LogicalLimit(cluster, traits, input, offset, fetch);
+    }
+
+    private LogicalLimit(RelOptCluster cluster, RelTraitSet traits, RelNode input, RexNode offset, RexNode fetch) {
+        super(cluster, traits, input, offset, fetch);
+    }
+    
+    @Override
+    public LogicalLimit copy(
+            RelTraitSet traitSet,
+            List<RelNode> newInputs) {
+        return create(
+                sole(newInputs),
+                offset,
+                fetch);
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixClientProject.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixClientProject.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixClientProject.java
index 58d3861..f10e97c 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixClientProject.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixClientProject.java
@@ -10,7 +10,6 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
@@ -18,6 +17,7 @@ import org.apache.hadoop.hbase.client.Scan;
 import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.compile.RowProjector;
 import org.apache.phoenix.compile.SequenceManager;
+import org.apache.phoenix.calcite.metadata.PhoenixRelMdCollation;
 import org.apache.phoenix.compile.OrderByCompiler.OrderBy;
 import org.apache.phoenix.compile.StatementContext;
 import org.apache.phoenix.execute.ClientScanPlan;
@@ -38,7 +38,7 @@ public class PhoenixClientProject extends PhoenixAbstractProject {
                 .replaceIfs(RelCollationTraitDef.INSTANCE,
                         new Supplier<List<RelCollation>>() {
                     public List<RelCollation> get() {
-                        return RelMdCollation.project(mq, input, projects);
+                        return PhoenixRelMdCollation.project(mq, input, projects);
                     }
                 });
         return new PhoenixClientProject(cluster, traits, input, projects, rowType);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixLimit.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixLimit.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixLimit.java
index 8406929..4c0f00e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixLimit.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixLimit.java
@@ -9,8 +9,6 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.RelWriter;
-import org.apache.calcite.rel.SingleRel;
 import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rex.RexLiteral;
@@ -22,9 +20,7 @@ import org.apache.phoenix.execute.ClientScanPlan;
 
 import com.google.common.base.Supplier;
 
-public class PhoenixLimit extends SingleRel implements PhoenixQueryRel {
-    public final RexNode offset;
-    public final RexNode fetch;
+public class PhoenixLimit extends Limit implements PhoenixQueryRel {
     
     public static PhoenixLimit create(final RelNode input, RexNode offset, RexNode fetch) {
         final RelOptCluster cluster = input.getCluster();
@@ -41,9 +37,7 @@ public class PhoenixLimit extends SingleRel implements PhoenixQueryRel {
     }
 
     private PhoenixLimit(RelOptCluster cluster, RelTraitSet traits, RelNode input, RexNode offset, RexNode fetch) {
-        super(cluster, traits, input);
-        this.offset = offset;
-        this.fetch = fetch;
+        super(cluster, traits, input, offset, fetch);
     }
     
     @Override
@@ -57,13 +51,6 @@ public class PhoenixLimit extends SingleRel implements PhoenixQueryRel {
     }
 
     @Override 
-    public RelWriter explainTerms(RelWriter pw) {
-        return super.explainTerms(pw)
-                .itemIf("offset", offset, offset != null)
-                .itemIf("fetch", fetch, fetch != null);
-    }
-
-    @Override 
     public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
         if (!getInput().getConvention().satisfies(PhoenixConvention.GENERIC))
             return planner.getCostFactory().makeInfiniteCost();
@@ -72,14 +59,6 @@ public class PhoenixLimit extends SingleRel implements PhoenixQueryRel {
                 .makeCost(rowCount, 0, 0)
                 .multiplyBy(PHOENIX_FACTOR);
     }
-    
-    @Override 
-    public double estimateRowCount(RelMetadataQuery mq) {
-        double rows = super.estimateRowCount(mq);
-        int offset = this.offset == null ? 0 : RexLiteral.intValue(this.offset);
-        int fetch = this.fetch == null ? Integer.MAX_VALUE : RexLiteral.intValue(this.fetch);
-        return Math.max(0, Math.min(fetch, rows - offset));
-    }
 
     @Override
     public QueryPlan implement(PhoenixRelImplementor implementor) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixServerProject.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixServerProject.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixServerProject.java
index 7932c3f..9290ffe 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixServerProject.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixServerProject.java
@@ -9,10 +9,10 @@ import org.apache.calcite.plan.RelTraitSet;
 import org.apache.calcite.rel.RelCollation;
 import org.apache.calcite.rel.RelCollationTraitDef;
 import org.apache.calcite.rel.RelNode;
-import org.apache.calcite.rel.metadata.RelMdCollation;
 import org.apache.calcite.rel.metadata.RelMetadataQuery;
 import org.apache.calcite.rel.type.RelDataType;
 import org.apache.calcite.rex.RexNode;
+import org.apache.phoenix.calcite.metadata.PhoenixRelMdCollation;
 import org.apache.phoenix.calcite.rel.PhoenixRelImplementor.ImplementorContext;
 import org.apache.phoenix.compile.QueryPlan;
 import org.apache.phoenix.execute.ScanPlan;
@@ -31,7 +31,7 @@ public class PhoenixServerProject extends PhoenixAbstractProject {
                 .replaceIfs(RelCollationTraitDef.INSTANCE,
                         new Supplier<List<RelCollation>>() {
                     public List<RelCollation> get() {
-                        return RelMdCollation.project(mq, input, projects);
+                        return PhoenixRelMdCollation.project(mq, input, projects);
                     }
                 });
         return new PhoenixServerProject(cluster, traits, input, projects, rowType);

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
index 4474bac..f57f974 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTableScan.java
@@ -2,6 +2,7 @@ package org.apache.phoenix.calcite.rel;
 
 import java.sql.SQLException;
 import java.util.List;
+import java.util.Objects;
 
 import org.apache.calcite.plan.RelOptCluster;
 import org.apache.calcite.plan.RelOptCost;
@@ -201,6 +202,26 @@ public class PhoenixTableScan extends TableScan implements PhoenixQueryRel {
             .itemIf("extendedColumns", extendedColumnRef, !extendedColumnRef.isEmpty());
     }
 
+    @Override public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof PhoenixTableScan)) {
+            return false;
+        }
+
+        PhoenixTableScan other = (PhoenixTableScan) obj;
+        return this.table.equals(other.table)
+                && Objects.equals(this.filter, other.filter)
+                && this.scanOrder == other.scanOrder
+                && this.extendedColumnRef.equals(other.extendedColumnRef);
+    }
+
+    @Override public int hashCode() {
+        return table.hashCode();
+    }
+
     @Override
     public RelOptCost computeSelfCost(RelOptPlanner planner, RelMetadataQuery mq) {
         double byteCount;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTemporarySort.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTemporarySort.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTemporarySort.java
index 596212f..74976fb 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTemporarySort.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rel/PhoenixTemporarySort.java
@@ -17,7 +17,7 @@ public class PhoenixTemporarySort extends PhoenixAbstractSort {
         RelOptCluster cluster = input.getCluster();
         collation = RelCollationTraitDef.INSTANCE.canonize(collation);
         RelTraitSet traits =
-                input.getTraitSet().replace(PhoenixConvention.SERVER).replace(collation);
+                input.getTraitSet().replace(input.getConvention()).replace(collation);
         return new PhoenixTemporarySort(cluster, traits, input, collation);
     }
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/36a31bf9/phoenix-core/src/main/java/org/apache/phoenix/calcite/rules/PhoenixConverterRules.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rules/PhoenixConverterRules.java b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rules/PhoenixConverterRules.java
index ca3e60e..4a8157b 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/calcite/rules/PhoenixConverterRules.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/calcite/rules/PhoenixConverterRules.java
@@ -40,6 +40,7 @@ import org.apache.calcite.rex.RexNode;
 import org.apache.calcite.sql.SqlKind;
 import org.apache.phoenix.calcite.CalciteUtils;
 import org.apache.phoenix.calcite.PhoenixTable;
+import org.apache.phoenix.calcite.rel.LogicalLimit;
 import org.apache.phoenix.calcite.rel.PhoenixAbstractAggregate;
 import org.apache.phoenix.calcite.rel.PhoenixClientAggregate;
 import org.apache.phoenix.calcite.rel.PhoenixClientJoin;
@@ -221,37 +222,22 @@ public class PhoenixConverterRules {
      */
     public static class PhoenixLimitRule extends PhoenixConverterRule {
         
-        private static Predicate<LogicalSort> HAS_FETCH = new Predicate<LogicalSort>() {
-            @Override
-            public boolean apply(LogicalSort input) {
-                return input.fetch != null || input.offset != null;
-            }
-        };
-        
         public static final PhoenixLimitRule INSTANCE = new PhoenixLimitRule();
 
         private PhoenixLimitRule() {
-            super(LogicalSort.class, 
-                    HAS_FETCH, 
+            super(LogicalLimit.class, 
                     Convention.NONE, PhoenixConvention.CLIENT, "PhoenixLimitRule");
         }
 
         public RelNode convert(RelNode rel) {
-            final LogicalSort sort = (LogicalSort) rel;
-            RelNode input = sort.getInput();
-            if (!sort.getCollation().getFieldCollations().isEmpty()) {
-                input = sort.copy(
-                            sort.getTraitSet(), 
-                            sort.getInput(), 
-                            sort.getCollation(), 
-                            null, null);
-            }
+            final LogicalLimit limit = (LogicalLimit) rel;
+            RelNode input = limit.getInput();
             return PhoenixLimit.create(
                 convert(
                         input, 
                         input.getTraitSet().replace(PhoenixConvention.GENERIC)),
-                sort.offset, 
-                sort.fetch);
+                limit.offset, 
+                limit.fetch);
         }
     }