diff --git a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveMaterializedViewLogicalPlanner.java b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveMaterializedViewLogicalPlanner.java index 77a0167bca2bd..b0a80e6ecb8a3 100644 --- a/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveMaterializedViewLogicalPlanner.java +++ b/presto-hive/src/test/java/com/facebook/presto/hive/TestHiveMaterializedViewLogicalPlanner.java @@ -530,10 +530,10 @@ public void testMaterializedViewForUnionAllWithOneSideMaterialized() MaterializedResult baseTable = computeActual(baseQuery); assertEquals(viewTable, baseTable); - assertPlan(getSession(), viewQuery, anyTree(values("ds", "orderkey"), anyTree( + assertPlan(getSession(), viewQuery, anyTree( constrainedTableScan(table2, ImmutableMap.of("ds", multipleValues(createVarcharType(10), utf8Slices("2019-01-02")))), - constrainedTableScan(view, ImmutableMap.of())))); + constrainedTableScan(view, ImmutableMap.of()))); } finally { queryRunner.execute("DROP MATERIALIZED VIEW IF EXISTS " + view); diff --git a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SimplifyPlanWithEmptyInput.java b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SimplifyPlanWithEmptyInput.java index c1afd02e559cc..1e7d5067a95ce 100644 --- a/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SimplifyPlanWithEmptyInput.java +++ b/presto-main/src/main/java/com/facebook/presto/sql/planner/optimizations/SimplifyPlanWithEmptyInput.java @@ -43,6 +43,7 @@ import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.IntStream; @@ -71,7 +72,7 @@ *
  *     - Empty Values
  * 
- * + *

* For outer join: replace with empty values if outer side is empty and project node if inner side is empty * For example: *

@@ -87,7 +88,7 @@
  *          assignments := NULL if output not in Scan, otherwise identity projection
  *          - Scan
  * 
- * + *

* For aggregation: if it has default output for empty input, stop and do not simplify, otherwise convert to empty values node *

  *     - Aggregation
@@ -95,8 +96,9 @@
  *          - Empty Values
  * 
* No change for this query plan - * - * For Union node: if it has only one non-empty input, convert to a project node. If all inputs are empty, convert to empty values node + *

+ * For Union node: if it has only one non-empty input, convert to a project node. If all inputs are empty, convert to empty values node. If more than one input is non-empty, + * remove the empty inputs and keep the union node and the non-empty inputs. */ public class SimplifyPlanWithEmptyInput @@ -223,6 +225,13 @@ else if (nonEmptyChildIndex.size() == 1) { builder.putAll(node.getVariableMapping().entrySet().stream().collect(toImmutableMap(entry -> entry.getKey(), entry -> entry.getValue().get(index)))); return new ProjectNode(node.getSourceLocation(), idAllocator.getNextId(), rewrittenChildren.get(index), builder.build(), LOCAL); } + else if (nonEmptyChildIndex.size() < node.getSources().size()) { + this.planChanged = true; + List nonEmptyInput = nonEmptyChildIndex.stream().map(x -> node.getSources().get(x)).collect(toImmutableList()); + Map> newOutputToInputs = node.getVariableMapping().entrySet().stream() + .collect(toImmutableMap(Map.Entry::getKey, entry -> nonEmptyChildIndex.stream().map(idx -> entry.getValue().get(idx)).collect(toImmutableList()))); + return new UnionNode(node.getSourceLocation(), idAllocator.getNextId(), nonEmptyInput, node.getOutputVariables(), newOutputToInputs); + } return node.replaceChildren(rewrittenChildren); } diff --git a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestSimplifyPlanWithEmptyInput.java b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestSimplifyPlanWithEmptyInput.java index fcd29299e9a05..67033c7da9e7e 100644 --- a/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestSimplifyPlanWithEmptyInput.java +++ b/presto-main/src/test/java/com/facebook/presto/sql/planner/optimizations/TestSimplifyPlanWithEmptyInput.java @@ -21,6 +21,7 @@ import static com.facebook.presto.SystemSessionProperties.SIMPLIFY_PLAN_WITH_EMPTY_INPUT; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.aggregation; +import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.exchange; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.expression; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.filter; import static com.facebook.presto.sql.planner.assertions.PlanMatchPattern.functionCall; @@ -137,6 +138,18 @@ public void testUnionWithEmptyInput() tableScan("lineitem", ImmutableMap.of("partkey", "partkey", "orderkey", "orderkey")))); } + @Test + public void testUnionMultipleNonEmptyInput() + { + assertPlan("select orderkey, partkey from lineitem union all select orderkey, custkey as partkey from orders where false union all select custkey, nationkey from customer", + enableOptimization(), + output( + ImmutableList.of("orderkey", "partkey"), + exchange( + tableScan("lineitem", ImmutableMap.of("partkey", "partkey", "orderkey", "orderkey")), + tableScan("customer", ImmutableMap.of("nationkey", "nationkey", "custkey", "custkey"))))); + } + @Test public void testSemiJoinEmptyFilterSource() {