Repository: incubator-drill Updated Branches: refs/heads/master d5b9b11c9 -> 66cf6e274
DRILL-1455: enable projection pushdown past join i) refactor project past filter to remove redundant code ii) adding unit tests for projection past join & hybrid cases like projection past filter & join iii) disable one TestExampleQueries#testSelectStartSubQueryJoinWithWhereClause until DRILL-1680 is fixed. Project: http://git-wip-us.apache.org/repos/asf/incubator-drill/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-drill/commit/66cf6e27 Tree: http://git-wip-us.apache.org/repos/asf/incubator-drill/tree/66cf6e27 Diff: http://git-wip-us.apache.org/repos/asf/incubator-drill/diff/66cf6e27 Branch: refs/heads/master Commit: 66cf6e274af2ac375b20530fd520f3ed32409101 Parents: d5b9b11 Author: Hanifi Gunes <hgu...@maprtech.com> Authored: Fri Nov 7 16:58:08 2014 -0800 Committer: Aman Sinha <asi...@maprtech.com> Committed: Mon Nov 10 18:18:47 2014 -0800 ---------------------------------------------------------------------- .../exec/planner/logical/DrillConditions.java | 36 ++++++ .../logical/DrillPushProjectPastFilterRule.java | 72 +---------- .../logical/DrillPushProjectPastJoinRule.java | 32 +++++ .../exec/planner/logical/DrillRuleSets.java | 13 +- .../org/apache/drill/TestExampleQueries.java | 23 ++-- .../org/apache/drill/TestProjectPushDown.java | 127 +++++++++++++++++-- .../test/resources/project/pushdown/empty0.json | 0 .../test/resources/project/pushdown/empty1.json | 0 .../test/resources/project/pushdown/empty2.json | 0 9 files changed, 215 insertions(+), 88 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillConditions.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillConditions.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillConditions.java new file mode 100644 index 0000000..310ef82 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillConditions.java @@ -0,0 +1,36 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.drill.exec.planner.logical; + +import org.eigenbase.rel.rules.PushProjector; +import org.eigenbase.rex.RexCall; +import org.eigenbase.rex.RexNode; + +public final class DrillConditions { + + public static PushProjector.ExprCondition PRESERVE_ITEM = new PushProjector.ExprCondition() { + @Override + public boolean test(RexNode expr) { + if (expr instanceof RexCall) { + RexCall call = (RexCall)expr; + return "item".equals(call.getOperator().getName().toLowerCase()); + } + return false; + } + }; +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastFilterRule.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastFilterRule.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastFilterRule.java index dcec68a..29e6559 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastFilterRule.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastFilterRule.java @@ -17,78 +17,16 @@ */ package org.apache.drill.exec.planner.logical; -import org.eigenbase.rel.FilterRel; -import org.eigenbase.rel.ProjectRel; -import org.eigenbase.rel.RelNode; +import org.eigenbase.rel.rules.PushProjectPastFilterRule; import org.eigenbase.rel.rules.PushProjector; import org.eigenbase.relopt.RelOptRule; -import org.eigenbase.relopt.RelOptRuleCall; -import org.eigenbase.rex.RexCall; -import org.eigenbase.rex.RexNode; -import org.eigenbase.rex.RexOver; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -public class DrillPushProjectPastFilterRule extends RelOptRule { +public class DrillPushProjectPastFilterRule extends PushProjectPastFilterRule { - private final static Logger logger = LoggerFactory.getLogger(DrillPushProjectPastFilterRule.class); + public final static RelOptRule INSTANCE = new DrillPushProjectPastFilterRule(DrillConditions.PRESERVE_ITEM); - public final static RelOptRule INSTANCE = new DrillPushProjectPastFilterRule(new PushProjector.ExprCondition() { - @Override - public boolean test(RexNode expr) { - if (expr instanceof RexCall) { - RexCall call = (RexCall)expr; - return "ITEM".equals(call.getOperator().getName()); - } - return false; - } - }); - - /** - * Expressions that should be preserved in the projection - */ - private final PushProjector.ExprCondition preserveExprCondition; - - private DrillPushProjectPastFilterRule(PushProjector.ExprCondition preserveExprCondition) { - super(RelOptHelper.any(ProjectRel.class, FilterRel.class)); - this.preserveExprCondition = preserveExprCondition; - } - - @Override - public void onMatch(RelOptRuleCall call) { - ProjectRel origProj; - FilterRel filterRel; - - if (call.rels.length == 2) { - origProj = call.rel(0); - filterRel = call.rel(1); - } else { - origProj = null; - filterRel = call.rel(0); - } - RelNode rel = filterRel.getChild(); - RexNode origFilter = filterRel.getCondition(); - - if ((origProj != null) && RexOver.containsOver(origProj.getProjects(), null)) { - // Cannot push project through filter if project contains a windowed - // aggregate -- it will affect row counts. Abort this rule - // invocation; pushdown will be considered after the windowed - // aggregate has been implemented. It's OK if the filter contains a - // windowed aggregate. - return; - } - - PushProjector pushProjector = createPushProjector(origProj, origFilter, rel, preserveExprCondition); - RelNode topProject = pushProjector.convertProject(null); - - if (topProject != null) { - call.transformTo(topProject); - } - } - - protected PushProjector createPushProjector(ProjectRel origProj, RexNode origFilter, RelNode rel, - PushProjector.ExprCondition preserveExprCondition) { - return new PushProjector(origProj, origFilter,rel, preserveExprCondition); + protected DrillPushProjectPastFilterRule(PushProjector.ExprCondition preserveExprCondition) { + super(preserveExprCondition); } } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastJoinRule.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastJoinRule.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastJoinRule.java new file mode 100644 index 0000000..7296d08 --- /dev/null +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillPushProjectPastJoinRule.java @@ -0,0 +1,32 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.drill.exec.planner.logical; + +import org.eigenbase.rel.rules.PushProjectPastJoinRule; +import org.eigenbase.rel.rules.PushProjector; +import org.eigenbase.relopt.RelOptRule; + +public class DrillPushProjectPastJoinRule extends PushProjectPastJoinRule { + + public static final RelOptRule INSTANCE = new DrillPushProjectPastJoinRule(DrillConditions.PRESERVE_ITEM); + + protected DrillPushProjectPastJoinRule(PushProjector.ExprCondition preserveExprCondition) { + super(preserveExprCondition); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillRuleSets.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillRuleSets.java b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillRuleSets.java index 7af541a..f4481cb 100644 --- a/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillRuleSets.java +++ b/exec/java-exec/src/main/java/org/apache/drill/exec/planner/logical/DrillRuleSets.java @@ -45,6 +45,7 @@ import org.eigenbase.rel.RelFactories; import org.eigenbase.rel.rules.PushFilterPastJoinRule; import org.eigenbase.rel.rules.PushFilterPastProjectRule; import org.eigenbase.rel.rules.PushJoinThroughJoinRule; +import org.eigenbase.rel.rules.PushProjectPastFilterRule; import org.eigenbase.rel.rules.PushProjectPastJoinRule; import org.eigenbase.rel.rules.RemoveDistinctAggregateRule; import org.eigenbase.rel.rules.RemoveDistinctRule; @@ -87,9 +88,15 @@ public class DrillRuleSets { DrillMergeProjectRule.getInstance(true, RelFactories.DEFAULT_PROJECT_FACTORY, context.getFunctionRegistry()), RemoveDistinctAggregateRule.INSTANCE, // // ReduceAggregatesRule.INSTANCE, // replaced by DrillReduceAggregatesRule - PushProjectPastJoinRule.INSTANCE, + + /* + Projection push-down related rules + */ // PushProjectPastFilterRule.INSTANCE, DrillPushProjectPastFilterRule.INSTANCE, +// PushProjectPastJoinRule.INSTANCE, + DrillPushProjectPastJoinRule.INSTANCE, + // SwapJoinRule.INSTANCE, // // PushJoinThroughJoinRule.RIGHT, // // PushJoinThroughJoinRule.LEFT, // @@ -109,8 +116,8 @@ public class DrillRuleSets { DrillLimitRule.INSTANCE, DrillSortRule.INSTANCE, DrillJoinRule.INSTANCE, - DrillUnionRule.INSTANCE - ,DrillReduceAggregatesRule.INSTANCE + DrillUnionRule.INSTANCE, + DrillReduceAggregatesRule.INSTANCE )); } return DRILL_BASIC_RULES; http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/test/java/org/apache/drill/TestExampleQueries.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestExampleQueries.java b/exec/java-exec/src/test/java/org/apache/drill/TestExampleQueries.java index a328ade..cd70b3c 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/TestExampleQueries.java +++ b/exec/java-exec/src/test/java/org/apache/drill/TestExampleQueries.java @@ -19,6 +19,7 @@ package org.apache.drill; import org.apache.drill.common.util.FileUtils; import org.apache.drill.exec.rpc.RpcException; +import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.assertEquals; @@ -374,15 +375,6 @@ public class TestExampleQueries extends BaseTestQuery{ " group by n.n_regionkey \n" + " order by n.n_regionkey; " ); - // select clause, where, on, group by, order by. - test(" select n.n_regionkey, count(*) as cnt \n" + - " from (select * from cp.`tpch/nation.parquet`) n \n" + - " join (select * from cp.`tpch/region.parquet`) r \n" + - " on n.n_regionkey = r.r_regionkey \n" + - " where n.n_nationkey > 10 \n" + - " group by n.n_regionkey \n" + - " order by n.n_regionkey; " ); - // Outer query use select *. Join condition in where clause. test(" select * \n" + " from (select * from cp.`tpch/nation.parquet`) n \n" + @@ -396,6 +388,19 @@ public class TestExampleQueries extends BaseTestQuery{ " on n.n_regionkey = r.r_regionkey " ); } + @Test + @Ignore("This test has been ignored until DRILL-1680 is fixed") + public void testSelectStartSubQueryJoinWithWhereClause() throws Exception { + // select clause, where, on, group by, order by. + test(" select n.n_regionkey, count(*) as cnt \n" + + " from (select * from cp.`tpch/nation.parquet`) n \n" + + " join (select * from cp.`tpch/region.parquet`) r \n" + + " on n.n_regionkey = r.r_regionkey \n" + + " where n.n_nationkey > 10 \n" + + " group by n.n_regionkey \n" + + " order by n.n_regionkey; " ); + } + @Test // DRILL-595 : Select * in CTE WithClause : regular columns appear in select clause, where, group by, order by. public void testDRILL_595WithClause() throws Exception { test(" with x as (select * from cp.`region.json`) \n" + http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/test/java/org/apache/drill/TestProjectPushDown.java ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/java/org/apache/drill/TestProjectPushDown.java b/exec/java-exec/src/test/java/org/apache/drill/TestProjectPushDown.java index 828ffe9..6e998a3 100644 --- a/exec/java-exec/src/test/java/org/apache/drill/TestProjectPushDown.java +++ b/exec/java-exec/src/test/java/org/apache/drill/TestProjectPushDown.java @@ -106,9 +106,7 @@ public class TestProjectPushDown extends PlanTestBase { } - private static final String pushDownSql = "select %s from cp.`%s` t"; - private static final String pushDownSqlWithFilter = pushDownSql + " where %s"; - private final String[] inputTypes = new String[] { + private final String[] TABLES = new String[] { "project/pushdown/empty.json", "project/pushdown/empty.csv", "tpch/lineitem.parquet" @@ -116,17 +114,124 @@ public class TestProjectPushDown extends PlanTestBase { @Test public void testProjectPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t"; final String projection = "t.trans_id, t.user_info.cust_id, t.marketing_info.keywords[0]"; final String expected = "\"columns\" : [ \"`trans_id`\", \"`user_info`.`cust_id`\", \"`marketing_info`.`keywords`[0]\" ],"; + + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, expected, projection, table)); + } + } + + @Test + public void testProjectPastFilterPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t where %s"; + final String projection = "t.trans_id, t.user_info.cust_id, t.marketing_info.keywords[0]"; final String filter = "t.another_field = 10 and t.columns[0] = 100 and t.columns[1] = t.other.columns[2]"; - final String expectedWithFilter = "\"columns\" : [ \"`another_field`\", \"`trans_id`\", \"`user_info`.`cust_id`\", \"`marketing_info`.`keywords`[0]\", \"`columns`[0]\", \"`columns`[1]\", \"`other`.`columns`[2]\" ],"; + final String expected = "\"columns\" : [ \"`another_field`\", \"`trans_id`\", \"`user_info`.`cust_id`\", \"`marketing_info`.`keywords`[0]\", \"`columns`[0]\", \"`columns`[1]\", \"`other`.`columns`[2]\" ],"; - for (String inputType:inputTypes) { - testPushDown(new PushDownTestInstance(pushDownSql, expected, projection, inputType)); - testPushDown(new PushDownTestInstance(pushDownSqlWithFilter, expectedWithFilter, projection, inputType, filter)); + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, expected, projection, table, filter)); } } + @Test + public void testProjectPastJoinPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t0, cp.`%s` t1 where %s"; + final String projection = "t0.fcolumns[0], t0.fmy.field, t0.freally.nested.field[0], t1.scolumns[0], t1.smy.field, t1.sreally.nested.field[0]"; + final String filter = "t0.fname = t1.sname and t0.fcolumns[1]=10 and t1.scolumns[1]=100"; + final String firstExpected = "\"columns\" : [ \"`fname`\", \"`fcolumns`[0]\", \"`fmy`.`field`\", \"`freally`.`nested`.`field`[0]\", \"`fcolumns`[1]\" ],"; + final String secondExpected = "\"columns\" : [ \"`sname`\", \"`scolumns`[0]\", \"`smy`.`field`\", \"`sreally`.`nested`.`field`[0]\", \"`scolumns`[1]\" ],"; + + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, new String[]{firstExpected, secondExpected}, + projection, table, table, filter)); + } + } + + @Test + public void testProjectPastFilterPastJoinPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t0, cp.`%s` t1 where %s"; + final String projection = "t0.fcolumns[0], t0.fmy.field, t0.freally.nested.field[0], t1.scolumns[0], t1.smy.field, t1.sreally.nested.field[0]"; + final String filter = "t0.fname = t1.sname and t0.fcolumns[1] + t1.scolumns[1]=100"; + final String firstExpected = "\"columns\" : [ \"`fname`\", \"`fcolumns`[0]\", \"`fmy`.`field`\", \"`freally`.`nested`.`field`[0]\", \"`fcolumns`[1]\" ],"; + final String secondExpected = "\"columns\" : [ \"`sname`\", \"`scolumns`[0]\", \"`smy`.`field`\", \"`sreally`.`nested`.`field`[0]\", \"`scolumns`[1]\" ],"; + + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, new String[]{firstExpected, secondExpected}, + projection, table, table, filter)); + } + } + + @Test + public void testProjectPastFilterPastJoinPushDownWhenItemsAreWithinNestedOperators() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t0, cp.`%s` t1 where %s"; + final String projection = "concat(t0.fcolumns[0], concat(t1.scolumns[0], t0.fmy.field, t0.freally.nested.field[0], t1.smy.field, t1.sreally.nested.field[0]))"; + final String filter = "t0.fname = t1.sname and t0.fcolumns[1] + t1.scolumns[1]=100"; + final String firstExpected = "\"columns\" : [ \"`fname`\", \"`fcolumns`[0]\", \"`fmy`.`field`\", \"`freally`.`nested`.`field`[0]\", \"`fcolumns`[1]\" ],"; + final String secondExpected = "\"columns\" : [ \"`sname`\", \"`scolumns`[0]\", \"`smy`.`field`\", \"`sreally`.`nested`.`field`[0]\", \"`scolumns`[1]\" ],"; + + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, new String[]{firstExpected, secondExpected}, + projection, table, table, filter)); + } + } + + @Test + public void testProjectPastFilterPastJoinPastJoinPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t0, cp.`%s` t1, cp.`%s` t2 where %s"; + final String projection = "t0.fcolumns[0], t0.fmy.field, t0.freally.nested.field[0], t1.scolumns[0], t1.smy.field, t1.sreally.nested.field[0], t2.tcolumns[0], t2.tmy.field, t2.treally.nested.field[0]"; + final String filter = "t0.fname = t1.sname and t1.slastname = t2.tlastname and t0.fcolumns[1] + t1.scolumns[1] + t2.tcolumns[1]=100"; + final String firstExpected = "\"columns\" : [ \"`fname`\", \"`fcolumns`[1]\", \"`fcolumns`[0]\", \"`fmy`.`field`\", \"`freally`.`nested`.`field`[0]\" ],"; + final String secondExpected = "\"columns\" : [ \"`sname`\", \"`slastname`\", \"`scolumns`[1]\", \"`scolumns`[0]\", \"`smy`.`field`\", \"`sreally`.`nested`.`field`[0]\" ],"; + final String thirdExpected = "\"columns\" : [ \"`tlastname`\", \"`tcolumns`[1]\", \"`tcolumns`[0]\", \"`tmy`.`field`\", \"`treally`.`nested`.`field`[0]\" ],"; + + for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, + new String[]{firstExpected, secondExpected, thirdExpected}, projection, table, table, table, filter)); + } + } + + @Test + @Ignore("This query does not work when ProjectPastJoin is enabled. This is a known issue.") + public void testProjectPastJoinPastFilterPastJoinPushDown() throws Exception { + final String pushDownSqlPattern = "select %s from cp.`%s` t0, cp.`%s` t1, cp.`%s` t2 where %s"; + final String projection = "t0.fcolumns[0], t0.fmy.field, t0.freally.nested.field[0], t1.scolumns[0], t1.smy.field, t1.sreally.nested.field[0], t2.tcolumns[0], t2.tmy.field, t2.treally.nested.field[0]"; + final String filter = "t0.fname = t1.sname and t1.slastname = t2.tlastname and t0.fcolumns[1] + t1.scolumns[1] = 100"; + final String firstExpected = "\"columns\" : [ \"`fname`\", \"`fcolumns`[1]\", \"`fcolumns`[0]\", \"`fmy`.`field`\", \"`freally`.`nested`.`field`[0]\" ],"; + final String secondExpected = "\"columns\" : [ \"`sname`\", \"`slastname`\", \"`scolumns`[1]\", \"`scolumns`[0]\", \"`smy`.`field`\", \"`sreally`.`nested`.`field`[0]\" ],"; + final String thirdExpected = "\"columns\" : [ \"`tlastname`\", \"`tcolumns`[1]\", \"`tcolumns`[0]\", \"`tmy`.`field`\", \"`treally`.`nested`.`field`[0]\" ],"; + + final String[] TABLES = new String[] { + "project/pushdown/empty0.json", + "project/pushdown/empty1.json", + "project/pushdown/empty2.json", + }; +// for (String table: TABLES) { + testPushDown(new PushDownTestInstance(pushDownSqlPattern, + new String[]{firstExpected, secondExpected, thirdExpected}, projection, TABLES[0], TABLES[1], TABLES[2], filter)); +// } + } + + @Test + @Ignore("This query does not work when ProjectPastJoin is enabled. This is a known issue.") + public void testSimpleProjectPastJoinPastFilterPastJoinPushDown() throws Exception { + String sql = "select * " + + "from cp.`%s` t0, cp.`%s` t1, cp.`%s` t2 " + + "where t0.fname = t1.sname and t1.slastname = t2.tlastname and t0.fcolumns[0] + t1.scolumns = 100"; + + final String[] TABLES = new String[] { + "project/pushdown/empty0.json", + "project/pushdown/empty1.json", + "project/pushdown/empty2.json", + }; + + sql = "select t0.fa, t1.sa, t2.ta " + + " from cp.`%s` t0, cp.`%s` t1, cp.`%s` t2 " + + " where t0.a=t1.b and t1.c=t2.d and t0.fcolumns[0] + t1.a = 100"; + testPushDown(new PushDownTestInstance(sql, "nothing", TABLES[0],TABLES[1],TABLES[2])); + } + protected void testPushDown(PushDownTestInstance test) throws Exception { testPhysicalPlan(test.getSql(), test.getExpected()); } @@ -145,16 +250,20 @@ public class TestProjectPushDown extends PlanTestBase { protected static class PushDownTestInstance { private final String sqlPattern; - private final String expected; + private final String[] expected; private final Object[] params; public PushDownTestInstance(String sqlPattern, String expected, Object... params) { + this(sqlPattern, new String[]{expected}, params); + } + + public PushDownTestInstance(String sqlPattern, String[] expected, Object... params) { this.sqlPattern = sqlPattern; this.expected = expected; this.params = params; } - public String getExpected() { + public String[] getExpected() { return expected; } http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/test/resources/project/pushdown/empty0.json ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/resources/project/pushdown/empty0.json b/exec/java-exec/src/test/resources/project/pushdown/empty0.json new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/test/resources/project/pushdown/empty1.json ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/resources/project/pushdown/empty1.json b/exec/java-exec/src/test/resources/project/pushdown/empty1.json new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/incubator-drill/blob/66cf6e27/exec/java-exec/src/test/resources/project/pushdown/empty2.json ---------------------------------------------------------------------- diff --git a/exec/java-exec/src/test/resources/project/pushdown/empty2.json b/exec/java-exec/src/test/resources/project/pushdown/empty2.json new file mode 100644 index 0000000..e69de29