tajo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hyun...@apache.org
Subject git commit: TAJO-830: Some filter conditions with a SUBQUERY are removed by optimizer. (Hyoungjun Kim via hyunsik)
Date Mon, 26 May 2014 18:55:54 GMT
Repository: tajo
Updated Branches:
  refs/heads/master 5bdfe887a -> e2d046474


TAJO-830: Some filter conditions with a SUBQUERY are removed by optimizer. (Hyoungjun Kim via hyunsik)

Closes #12


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

Branch: refs/heads/master
Commit: e2d0464747a08dd1c0df5fd6010c878d66802ace
Parents: 5bdfe88
Author: Hyunsik Choi <hyunsik@apache.org>
Authored: Tue May 27 03:54:56 2014 +0900
Committer: Hyunsik Choi <hyunsik@apache.org>
Committed: Tue May 27 03:54:56 2014 +0900

----------------------------------------------------------------------
 CHANGES                                         |   3 +
 .../org/apache/tajo/jdbc/TajoResultSet.java     |   3 +
 .../java/org/apache/tajo/conf/TajoConf.java     |   5 +-
 .../engine/planner/BasicLogicalPlanVisitor.java |   2 +-
 .../tajo/engine/planner/LogicalOptimizer.java   |   5 +-
 .../apache/tajo/engine/planner/LogicalPlan.java |   6 +
 .../engine/planner/LogicalPlanPreprocessor.java |   6 +-
 .../tajo/engine/planner/LogicalPlanner.java     |   4 +-
 .../engine/planner/logical/GroupbyNode.java     |  14 +
 .../planner/rewrite/FilterPushDownRule.java     | 608 +++++++++++++++++--
 .../tajo/engine/query/TestUnionQuery.java       |  53 ++
 .../testJoinCoReferredEvalsFilterPushdown.sql   |  13 +
 .../queries/TestSelectQuery/testWhereCond2.sql  |   5 +-
 .../queries/TestUnionQuery/testUnion11.sql      |   8 +
 .../queries/TestUnionQuery/testUnion12.sql      |  13 +
 .../queries/TestUnionQuery/testUnion13.sql      |  14 +
 .../queries/TestUnionQuery/testUnion14.sql      |   8 +
 .../queries/TestUnionQuery/testUnion15.sql      |  15 +
 .../queries/TestUnionQuery/testUnion16.sql      |  15 +
 .../TestSelectQuery/testWhereCond2.result       |   6 +-
 .../results/TestUnionQuery/testUnion11.result   |   3 +
 .../results/TestUnionQuery/testUnion12.result   |   6 +
 .../results/TestUnionQuery/testUnion13.result   |   6 +
 .../results/TestUnionQuery/testUnion14.result   |   9 +
 .../results/TestUnionQuery/testUnion15.result   |   5 +
 .../results/TestUnionQuery/testUnion16.result   |   5 +
 26 files changed, 770 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 5645b13..d1c72aa 100644
--- a/CHANGES
+++ b/CHANGES
@@ -43,6 +43,9 @@ Release 0.9.0 - unreleased
 
   BUG FIXES
 
+    TAJO-830: Some filter conditions with a SUBQUERY are removed by optimizer.
+    (Hyoungjun Kim via hyunsik)
+
     TAJO-819: KillQuery does not work for running query on TajoWorker. (jaehwa)
 
     TAJO-808: Fix pre-commit build failure. (jinho)

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
----------------------------------------------------------------------
diff --git a/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java b/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
index 336c782..8595970 100644
--- a/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
+++ b/tajo-client/src/main/java/org/apache/tajo/jdbc/TajoResultSet.java
@@ -87,6 +87,9 @@ public class TajoResultSet extends TajoResultSetBase {
         this.totalRow = desc.getStats() != null ? desc.getStats().getNumRows() : INFINITE_ROW_NUM;
       }
 
+      if (totalRow == 0) {
+        totalRow = INFINITE_ROW_NUM;
+      }
 
       List<FileFragment> frags = getFragments(desc.getPath());
       scanner = new MergeScanner(conf, desc.getSchema(), desc.getMeta(), frags);

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
----------------------------------------------------------------------
diff --git a/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java b/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
index b62b246..e5f6ca1 100644
--- a/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
+++ b/tajo-common/src/main/java/org/apache/tajo/conf/TajoConf.java
@@ -327,7 +327,10 @@ public class TajoConf extends Configuration {
 
     //TIME & DATE
     TAJO_TIMEZONE("tajo.timezone", System.getProperty("user.timezone")),
-    TAJO_DATE_ORDER("tajo.date.order", "YMD")
+    TAJO_DATE_ORDER("tajo.date.order", "YMD"),
+
+    //PLANNER
+    PLANNER_USE_FILTER_PUSHDOWN("tajo.planner.use.filter.pushdown", true)
     ;
 
     public final String varname;

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
index 3bffefb..331b865 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
@@ -252,7 +252,7 @@ public class BasicLogicalPlanVisitor<CONTEXT, RESULT> implements LogicalPlanVisi
                                    TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
     stack.push(node);
     LogicalPlan.QueryBlock childBlock = plan.getBlock(node.getSubQuery());
-    RESULT result = visit(context, plan, childBlock, childBlock.getRoot(), new Stack<LogicalNode>());
+    RESULT result = visit(context, plan, childBlock, childBlock.getRoot(), stack);
     stack.pop();
     return result;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalOptimizer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalOptimizer.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalOptimizer.java
index 974dc60..3bf70a7 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalOptimizer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalOptimizer.java
@@ -23,6 +23,7 @@ import com.google.common.collect.Sets;
 import org.apache.hadoop.classification.InterfaceStability;
 import org.apache.tajo.algebra.JoinType;
 import org.apache.tajo.conf.TajoConf;
+import org.apache.tajo.conf.TajoConf.ConfVars;
 import org.apache.tajo.engine.eval.AlgebraicUtil;
 import org.apache.tajo.engine.eval.EvalNode;
 import org.apache.tajo.engine.planner.graph.DirectedGraphCursor;
@@ -54,7 +55,9 @@ public class LogicalOptimizer {
 
   public LogicalOptimizer(TajoConf systemConf) {
     rulesBeforeJoinOpt = new BasicQueryRewriteEngine();
-    rulesBeforeJoinOpt.addRewriteRule(new FilterPushDownRule());
+    if (systemConf.getBoolVar(ConfVars.PLANNER_USE_FILTER_PUSHDOWN)) {
+      rulesBeforeJoinOpt.addRewriteRule(new FilterPushDownRule());
+    }
 
     rulesAfterToJoinOpt = new BasicQueryRewriteEngine();
     rulesAfterToJoinOpt.addRewriteRule(new ProjectionPushDownRule());

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
index 6be0c6a..519f594 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
@@ -420,6 +420,12 @@ public class LogicalPlan {
       return ensureUniqueColumn(candidates);
     }
 
+    // This is an exception case. It means that there are some bugs in other parts.
+    LogicalNode blockRootNode = block.getRoot();
+    if (blockRootNode != null && blockRootNode.getOutSchema().getColumn(columnRef.getCanonicalName()) != null) {
+      throw new VerifyException("ERROR: no such a column name "+ columnRef.getCanonicalName());
+    }
+
     // Trying to find columns from other relations in other blocks
     for (QueryBlock eachBlock : queryBlocks.values()) {
       for (RelationNode rel : eachBlock.getRelations()) {

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
index 56863f7..a061b5a 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
@@ -289,11 +289,13 @@ class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanPreprocessor
     LogicalPlan.QueryBlock leftBlock = ctx.plan.newQueryBlock();
     PreprocessContext leftContext = new PreprocessContext(ctx, leftBlock);
     LogicalNode leftChild = visit(leftContext, new Stack<Expr>(), expr.getLeft());
+    leftBlock.setRoot(leftChild);
     ctx.currentBlock.registerExprWithNode(expr.getLeft(), leftChild);
 
     LogicalPlan.QueryBlock rightBlock = ctx.plan.newQueryBlock();
     PreprocessContext rightContext = new PreprocessContext(ctx, rightBlock);
     LogicalNode rightChild = visit(rightContext, new Stack<Expr>(), expr.getRight());
+    rightBlock.setRoot(rightChild);
     ctx.currentBlock.registerExprWithNode(expr.getRight(), rightChild);
 
     UnionNode unionNode = new UnionNode(ctx.plan.newPID());
@@ -363,8 +365,10 @@ class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanPreprocessor
     PreprocessContext newContext;
     // Note: TableSubQuery always has a table name.
     // SELECT .... FROM (SELECT ...) TB_NAME <-
-    newContext = new PreprocessContext(ctx, ctx.plan.newQueryBlock());
+    QueryBlock queryBlock = ctx.plan.newQueryBlock();
+    newContext = new PreprocessContext(ctx, queryBlock);
     LogicalNode child = super.visitTableSubQuery(newContext, stack, expr);
+    queryBlock.setRoot(child);
 
     // a table subquery should be dealt as a relation.
     TableSubQueryNode node = ctx.plan.createNode(TableSubQueryNode.class);

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
index d5d2d47..0780d4f 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
@@ -584,7 +584,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     stack.pop();
     ////////////////////////////////////////////////////////
 
-    HavingNode having = new HavingNode(context.plan.newPID());
+    HavingNode having = context.queryBlock.getNodeFromExpr(expr);
     having.setChild(child);
     having.setInSchema(child.getOutSchema());
     having.setOutSchema(child.getOutSchema());
@@ -1310,7 +1310,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
   @Override
   public LogicalNode visitDropDatabase(PlanContext context, Stack<Expr> stack, DropDatabase expr)
       throws PlanningException {
-    DropDatabaseNode dropDatabaseNode = context.plan.createNode(DropDatabaseNode.class);
+    DropDatabaseNode dropDatabaseNode = context.queryBlock.getNodeFromExpr(expr);
     dropDatabaseNode.init(expr.getDatabaseName(), expr.isIfExists());
     return dropDatabaseNode;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
index 828b06d..75aa52c 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
@@ -237,4 +237,18 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
 
     return planStr;
   }
+
+  /**
+   * It checks if an alias name included in the target of this node is for aggregation function.
+   * If so, it returns TRUE. Otherwise, it returns FALSE.
+   */
+  public boolean isAggregationColumn(String simpleName) {
+    for (int i = groupingColumns.length; i < targets.length; i++) {
+      if (simpleName.equals(targets[i].getNamedColumn().getSimpleName()) ||
+          simpleName.equals(targets[i].getAlias())) {
+        return true;
+      }
+    }
+    return false;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
index 32d4f34..b848c86 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
@@ -20,19 +20,57 @@ package org.apache.tajo.engine.planner.rewrite;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.tajo.algebra.JoinType;
 import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
 import org.apache.tajo.engine.eval.*;
 import org.apache.tajo.engine.exception.InvalidQueryException;
 import org.apache.tajo.engine.planner.*;
 import org.apache.tajo.engine.planner.logical.*;
+import org.apache.tajo.engine.planner.rewrite.FilterPushDownRule.FilterPushDownContext;
 import org.apache.tajo.util.TUtil;
 
 import java.util.*;
 
-public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, LogicalNode> implements RewriteRule {
+/**
+ * This rule tries to push down all filter conditions into logical nodes as lower as possible.
+ * It is likely to significantly reduces the intermediate data.
+ */
+public class FilterPushDownRule extends BasicLogicalPlanVisitor<FilterPushDownContext, LogicalNode>
+    implements RewriteRule {
+  private final static Log LOG = LogFactory.getLog(FilterPushDownRule.class);
   private static final String NAME = "FilterPushDown";
 
+  static class FilterPushDownContext {
+    Set<EvalNode> pushingDownFilters = new HashSet<EvalNode>();
+
+    public void clear() {
+      pushingDownFilters.clear();
+    }
+    public void setFiltersTobePushed(Collection<EvalNode> workingEvals) {
+      this.pushingDownFilters.clear();
+      this.pushingDownFilters.addAll(workingEvals);
+    }
+    public void addFiltersTobePushed(Collection<EvalNode> workingEvals) {
+      this.pushingDownFilters.addAll(workingEvals);
+    }
+
+    public void setToOrigin(Map<EvalNode, EvalNode> evalMap) {
+      //evalMap: copy -> origin
+      List<EvalNode> origins = new ArrayList<EvalNode>();
+      for (EvalNode eval : pushingDownFilters) {
+        EvalNode origin = evalMap.get(eval);
+        if (origin != null) {
+          origins.add(origin);
+        }
+      }
+      setFiltersTobePushed(origins);
+    }
+  }
+
   @Override
   public String getName() {
     return NAME;
@@ -50,23 +88,41 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
 
   @Override
   public LogicalPlan rewrite(LogicalPlan plan) throws PlanningException {
+    /*
+    FilterPushDown rule: processing when visits each node
+      - If a target which is corresponding on a filter EvalNode's column is not FieldEval, do not PushDown.
+      - Replace filter EvalNode's column with child node's output column.
+        If there is no child node's output column, do not PushDown.
+      - When visit ScanNode, add filter eval to ScanNode's qual
+      - When visit GroupByNode, Find aggregation column in a filter EvalNode and
+        . If a parent is HavingNode, add filter eval to parent HavingNode.
+        . It not, create new HavingNode and set parent's child.
+     */
+    FilterPushDownContext context = new FilterPushDownContext();
     for (LogicalPlan.QueryBlock block : plan.getQueryBlocks()) {
-      this.visit(new HashSet<EvalNode>(), plan, block, block.getRoot(), new Stack<LogicalNode>());
+      context.clear();
+      this.visit(context, plan, block, block.getRoot(), new Stack<LogicalNode>());
     }
 
+    if (LOG.isDebugEnabled()) {
+      LOG.debug("=============================================");
+      LOG.debug("FilterPushDown Optimized Query: \n" + plan.toString());
+      LOG.debug("=============================================");
+    }
     return plan;
   }
 
   @Override
-  public LogicalNode visitFilter(Set<EvalNode> cnf, LogicalPlan plan, LogicalPlan.QueryBlock block,
+  public LogicalNode visitFilter(FilterPushDownContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
                                  SelectionNode selNode, Stack<LogicalNode> stack) throws PlanningException {
-    cnf.addAll(Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(selNode.getQual())));
+    context.pushingDownFilters.addAll(Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(selNode.getQual())));
 
     stack.push(selNode);
-    visit(cnf, plan, block, selNode.getChild(), stack);
+    visit(context, plan, block, selNode.getChild(), stack);
     stack.pop();
 
-    if(cnf.size() == 0) { // remove the selection operator if there is no search condition after selection push.
+    if(context.pushingDownFilters.size() == 0) {
+      // remove the selection operator if there is no search condition after selection push.
       LogicalNode node = stack.peek();
       if (node instanceof UnaryNode) {
         UnaryNode unary = (UnaryNode) node;
@@ -78,16 +134,17 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
 
       // check if it can be evaluated here
       Set<EvalNode> matched = TUtil.newHashSet();
-      for (EvalNode eachEval : cnf) {
+      for (EvalNode eachEval : context.pushingDownFilters) {
         if (LogicalPlanner.checkIfBeEvaluatedAtThis(eachEval, selNode)) {
           matched.add(eachEval);
         }
       }
 
-      // if there are search conditions which can be evaluated here, push down them and remove them from cnf.
+      // if there are search conditions which can be evaluated here,
+      // push down them and remove them from context.pushingDownFilters.
       if (matched.size() > 0) {
         selNode.setQual(AlgebraicUtil.createSingletonExprFromCNF(matched.toArray(new EvalNode[matched.size()])));
-        cnf.removeAll(matched);
+        context.pushingDownFilters.removeAll(matched);
       }
     }
 
@@ -99,7 +156,7 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
   }
 
   @Override
-  public LogicalNode visitJoin(Set<EvalNode> cnf, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode joinNode,
+  public LogicalNode visitJoin(FilterPushDownContext context, LogicalPlan plan, LogicalPlan.QueryBlock block, JoinNode joinNode,
                                Stack<LogicalNode> stack) throws PlanningException {
     LogicalNode left = joinNode.getRightChild();
     LogicalNode right = joinNode.getLeftChild();
@@ -157,7 +214,7 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
         // retain in this outer join node's JoinQual those selection predicates
         // related to the outer join's null supplier(s)
         List<EvalNode> matched2 = Lists.newArrayList();
-        for (EvalNode eval : cnf) {
+        for (EvalNode eval : context.pushingDownFilters) {
 
           Set<Column> columnRefs = EvalTreeUtil.findUniqueColumns(eval);
           Set<String> tableNames = Sets.newHashSet();
@@ -183,7 +240,8 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
 
         }
 
-        //merge the retained predicates and establish them in the current outer join node. Then remove them from the cnf
+        // merge the retained predicates and establish them in the current outer join node.
+        // Then remove them from the pushingDownFilters
         EvalNode qual2 = null;
         if (matched2.size() > 1) {
           // merged into one eval tree
@@ -197,20 +255,35 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
         if (qual2 != null) {
           EvalNode conjQual2 = AlgebraicUtil.createSingletonExprFromCNF(joinNode.getJoinQual(), qual2);
           joinNode.setJoinQual(conjQual2);
-          cnf.removeAll(matched2);
-        } // for the remaining cnf, push it as usual
+          context.pushingDownFilters.removeAll(matched2);
+        } // for the remaining context.pushingDownFilters, push it as usual
       }
     }
 
     if (joinNode.hasJoinQual()) {
-      cnf.addAll(Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual())));
+      context.addFiltersTobePushed(Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual())));
     }
 
-    visit(cnf, plan, block, left, stack);
-    visit(cnf, plan, block, right, stack);
+    List<EvalNode> notMatched = new ArrayList<EvalNode>();
+    // Join's input schema = right child output columns + left child output columns
+    Map<EvalNode, EvalNode> transformedMap = findCanPushdownAndTransform(context, joinNode, left, notMatched, true,
+        right.getOutSchema().size());
+    context.setFiltersTobePushed(transformedMap.keySet());
+    visit(context, plan, block, left, stack);
+
+    context.setToOrigin(transformedMap);
+    context.addFiltersTobePushed(notMatched);
+
+    transformedMap = findCanPushdownAndTransform(context, joinNode, right, notMatched, true, 0);
+    context.setFiltersTobePushed(new HashSet<EvalNode>(transformedMap.keySet()));
+
+    visit(context, plan, block, right, stack);
+
+    context.setToOrigin(transformedMap);
+    context.addFiltersTobePushed(notMatched);
 
     List<EvalNode> matched = Lists.newArrayList();
-    for (EvalNode eval : cnf) {
+    for (EvalNode eval : context.pushingDownFilters) {
       if (LogicalPlanner.checkIfBeEvaluatedAtJoin(block, eval, joinNode, stack.peek().getType() != NodeType.JOIN)) {
         matched.add(eval);
       }
@@ -232,50 +305,85 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
       if (joinNode.getJoinType() == JoinType.CROSS) {
         joinNode.setJoinType(JoinType.INNER);
       }
-      cnf.removeAll(matched);
+      context.pushingDownFilters.removeAll(matched);
     }
 
     return joinNode;
   }
 
-  @Override
-  public LogicalNode visitTableSubQuery(Set<EvalNode> cnf, LogicalPlan plan, LogicalPlan.QueryBlock block,
-                                        TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
-    List<EvalNode> matched = Lists.newArrayList();
-    for (EvalNode eval : cnf) {
-      if (LogicalPlanner.checkIfBeEvaluatedAtRelation(block, eval, node)) {
-        matched.add(eval);
+  private Map<EvalNode, EvalNode> transformEvalsWidthByPassNode(
+      Collection<EvalNode> originEvals, LogicalPlan plan,
+      LogicalPlan.QueryBlock block,
+      LogicalNode node, LogicalNode childNode) throws PlanningException {
+    // transformed -> pushingDownFilters
+    Map<EvalNode, EvalNode> transformedMap = new HashMap<EvalNode, EvalNode>();
+
+    if (originEvals.isEmpty()) {
+      return transformedMap;
+    }
+
+    if (node.getType() == NodeType.UNION) {
+      // If node is union, All eval's columns are simple name and matched with child's output schema.
+      Schema childOutSchema = childNode.getOutSchema();
+      for (EvalNode eval : originEvals) {
+        EvalNode copy;
+        try {
+          copy = (EvalNode) eval.clone();
+        } catch (CloneNotSupportedException e) {
+          throw new PlanningException(e);
+        }
+
+        Set<Column> columns = EvalTreeUtil.findUniqueColumns(copy);
+        for (Column c : columns) {
+          Column column = childOutSchema.getColumn(c.getSimpleName());
+          if (column == null) {
+            throw new PlanningException(
+                "Invalid Filter PushDown on SubQuery: No such a corresponding column '"
+                    + c.getQualifiedName() + " for FilterPushDown(" + eval + "), " +
+                    "(PID=" + node.getPID() + ", Child=" + childNode.getPID() + ")");
+          }
+          EvalTreeUtil.changeColumnRef(copy, c.getSimpleName(), column.getQualifiedName());
+        }
+
+        transformedMap.put(copy, eval);
       }
+      return transformedMap;
     }
 
-    Map<String, String> columnMap = new HashMap<String, String>();
-    for (int i = 0; i < node.getInSchema().size(); i++) {
-      LogicalNode childNode = node.getSubQuery();
-      if (childNode.getOutSchema().getColumn(i).hasQualifier()) {
-      columnMap.put(node.getInSchema().getColumn(i).getQualifiedName(),
-          childNode.getOutSchema().getColumn(i).getQualifiedName());
-      } else {
-        NamedExprsManager namedExprsMgr = plan.getBlock(node.getSubQuery()).getNamedExprsManager();
-        String originalName = namedExprsMgr.getOriginalName(childNode.getOutSchema().getColumn(i)
-            .getQualifiedName());
-
-        // We need to consider aliased columns of sub-query.
-        // Because we can't get original column name for a special occasion.
-        // For example, if we use an aliased name inside a sub-query and then we use it to where
-        // condition outside the sub-query, we can't find its original name.
-        if (originalName != null) {
-          columnMap.put(node.getInSchema().getColumn(i).getQualifiedName(), originalName);
-        } else {
-          columnMap.put(node.getInSchema().getColumn(i).getQualifiedName(),
-            node.getInSchema().getColumn(i).getQualifiedName());
+    if (childNode.getType() == NodeType.UNION) {
+      // If child is union, remove qualifier from eval's column
+      for (EvalNode eval : originEvals) {
+        EvalNode copy;
+        try {
+          copy = (EvalNode) eval.clone();
+        } catch (CloneNotSupportedException e) {
+          throw new PlanningException(e);
         }
+
+        Set<Column> columns = EvalTreeUtil.findUniqueColumns(copy);
+        for (Column c : columns) {
+          if (c.hasQualifier()) {
+            EvalTreeUtil.changeColumnRef(copy, c.getQualifiedName(), c.getSimpleName());
+          }
+        }
+
+        transformedMap.put(copy, eval);
       }
+
+      return transformedMap;
     }
 
-    Set<EvalNode> transformed = new HashSet<EvalNode>();
+    // node in column -> child out column
+    Map<String, String> columnMap = new HashMap<String, String>();
+
+    for (int i = 0; i < node.getInSchema().size(); i++) {
+      String inColumnName = node.getInSchema().getColumn(i).getQualifiedName();
+      Column childOutColumn = childNode.getOutSchema().getColumn(i);
+      columnMap.put(inColumnName, childOutColumn.getQualifiedName());
+    }
 
     // Rename from upper block's one to lower block's one
-    for (EvalNode matchedEval : matched) {
+    for (EvalNode matchedEval : originEvals) {
       EvalNode copy;
       try {
         copy = (EvalNode) matchedEval.clone();
@@ -284,31 +392,397 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
       }
 
       Set<Column> columns = EvalTreeUtil.findUniqueColumns(copy);
+      boolean allMatched = true;
       for (Column c : columns) {
         if (columnMap.containsKey(c.getQualifiedName())) {
           EvalTreeUtil.changeColumnRef(copy, c.getQualifiedName(), columnMap.get(c.getQualifiedName()));
         } else {
-          throw new PlanningException(
-              "Invalid Filter PushDown on SubQuery: No such a corresponding column '"
-                  + c.getQualifiedName());
+          if (childNode.getType() == NodeType.GROUP_BY) {
+            if (((GroupbyNode) childNode).isAggregationColumn(c.getSimpleName())) {
+              allMatched = false;
+              break;
+            }
+          } else {
+            throw new PlanningException(
+                "Invalid Filter PushDown on SubQuery: No such a corresponding column '"
+                    + c.getQualifiedName() + " for FilterPushDown(" + matchedEval + "), " +
+                    "(PID=" + node.getPID() + ", Child=" + childNode.getPID() + ")"
+            );
+          }
         }
       }
+      if (allMatched) {
+        transformedMap.put(copy, matchedEval);
+      }
+    }
 
-      transformed.add(copy);
+    return transformedMap;
+  }
+
+  @Override
+  public LogicalNode visitTableSubQuery(FilterPushDownContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+                                        TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
+    List<EvalNode> matched = Lists.newArrayList();
+    for (EvalNode eval : context.pushingDownFilters) {
+      if (LogicalPlanner.checkIfBeEvaluatedAtRelation(block, eval, node)) {
+        matched.add(eval);
+      }
     }
 
-    visit(transformed, plan, plan.getBlock(node.getSubQuery()));
+    // transformed -> pushingDownFilters
+    Map<EvalNode, EvalNode> transformedMap =
+        transformEvalsWidthByPassNode(matched, plan, block, node, node.getSubQuery());
+
+    context.setFiltersTobePushed(new HashSet<EvalNode>(transformedMap.keySet()));
+    visit(context, plan, plan.getBlock(node.getSubQuery()));
 
-    cnf.removeAll(matched);
+    context.setToOrigin(transformedMap);
 
     return node;
   }
 
   @Override
-  public LogicalNode visitScan(Set<EvalNode> cnf, LogicalPlan plan, LogicalPlan.QueryBlock block, ScanNode scanNode,
+  public LogicalNode visitUnion(FilterPushDownContext context, LogicalPlan plan,
+                                LogicalPlan.QueryBlock block, UnionNode unionNode,
+                                Stack<LogicalNode> stack) throws PlanningException {
+    LogicalNode leftNode = unionNode.getLeftChild();
+
+    List<EvalNode> origins = new ArrayList<EvalNode>(context.pushingDownFilters);
+
+    // transformed -> pushingDownFilters
+    Map<EvalNode, EvalNode> transformedMap = transformEvalsWidthByPassNode(origins, plan, block, unionNode, leftNode);
+    context.setFiltersTobePushed(new HashSet<EvalNode>(transformedMap.keySet()));
+    visit(context, plan, plan.getBlock(leftNode));
+
+    if (!context.pushingDownFilters.isEmpty()) {
+      errorFilterPushDown(plan, leftNode, context);
+    }
+
+    LogicalNode rightNode = unionNode.getRightChild();
+    transformedMap = transformEvalsWidthByPassNode(origins, plan, block, unionNode, rightNode);
+    context.setFiltersTobePushed(new HashSet<EvalNode>(transformedMap.keySet()));
+    visit(context, plan, plan.getBlock(rightNode), rightNode, stack);
+
+    if (!context.pushingDownFilters.isEmpty()) {
+      errorFilterPushDown(plan, rightNode, context);
+    }
+
+    // notify all filter matched to upper
+    context.pushingDownFilters.clear();
+    return unionNode;
+  }
+
+  @Override
+  public LogicalNode visitProjection(FilterPushDownContext context,
+                                     LogicalPlan plan,
+                                     LogicalPlan.QueryBlock block,
+                                     ProjectionNode projectionNode,
+                                     Stack<LogicalNode> stack) throws PlanningException {
+    LogicalNode childNode = projectionNode.getChild();
+
+    List<EvalNode> notMatched = new ArrayList<EvalNode>();
+
+    //copy -> origin
+    Map<EvalNode, EvalNode> matched = findCanPushdownAndTransform(
+        context, projectionNode, childNode, notMatched, false, 0);
+
+    context.setFiltersTobePushed(matched.keySet());
+
+    stack.push(projectionNode);
+    LogicalNode current = visit(context, plan, plan.getBlock(childNode), childNode, stack);
+    stack.pop();
+
+    // find not matched after visiting child
+    for (EvalNode eval: context.pushingDownFilters) {
+      notMatched.add(matched.get(eval));
+    }
+
+    EvalNode qual = null;
+    if (notMatched.size() > 1) {
+      // merged into one eval tree
+      qual = AlgebraicUtil.createSingletonExprFromCNF(notMatched.toArray(new EvalNode[notMatched.size()]));
+    } else if (notMatched.size() == 1) {
+      // if the number of matched expr is one
+      qual = notMatched.get(0);
+    }
+
+    // If there is not matched node add SelectionNode and clear context.pushingDownFilters
+    if (qual != null) {
+      SelectionNode selectionNode = plan.createNode(SelectionNode.class);
+      selectionNode.setInSchema(current.getOutSchema());
+      selectionNode.setOutSchema(current.getOutSchema());
+      selectionNode.setQual(qual);
+      block.registerNode(selectionNode);
+
+      projectionNode.setChild(selectionNode);
+      selectionNode.setChild(current);
+    }
+
+    //notify all eval matched to upper
+    context.pushingDownFilters.clear();
+
+    return current;
+  }
+
+  private Map<EvalNode, EvalNode> findCanPushdownAndTransform(
+      FilterPushDownContext context, Projectable node,
+      LogicalNode childNode, List<EvalNode> notMatched,
+      boolean ignoreJoin, int columnOffset) throws PlanningException {
+    // canonical name -> target
+    Map<String, Target> nodeTargetMap = new HashMap<String, Target>();
+    for (Target target : node.getTargets()) {
+      nodeTargetMap.put(target.getCanonicalName(), target);
+    }
+
+    // copy -> origin
+    Map<EvalNode, EvalNode> matched = new HashMap<EvalNode, EvalNode>();
+
+    for (EvalNode eval : context.pushingDownFilters) {
+      if (ignoreJoin && EvalTreeUtil.isJoinQual(eval, true)) {
+        notMatched.add(eval);
+        continue;
+      }
+      // If all column is field eval, can push down.
+      Set<Column> evalColumns = EvalTreeUtil.findUniqueColumns(eval);
+      boolean columnMatched = true;
+      for (Column c : evalColumns) {
+        Target target = nodeTargetMap.get(c.getQualifiedName());
+        if (target == null) {
+          columnMatched = false;
+          break;
+        }
+        if (target.getEvalTree().getType() != EvalType.FIELD) {
+          columnMatched = false;
+          break;
+        }
+      }
+
+      if (columnMatched) {
+        // transform eval column to child's output column
+        EvalNode copyEvalNode = transformEval(node, childNode, eval, nodeTargetMap, columnOffset);
+        if (copyEvalNode != null) {
+          matched.put(copyEvalNode, eval);
+        } else {
+          notMatched.add(eval);
+        }
+      } else {
+        notMatched.add(eval);
+      }
+    }
+
+    return matched;
+  }
+
+  private EvalNode transformEval(Projectable node, LogicalNode childNode, EvalNode origin,
+                                 Map<String, Target> targetMap, int columnOffset) throws PlanningException {
+    Schema outputSchema = childNode != null ? childNode.getOutSchema() : node.getInSchema();
+    EvalNode copy;
+    try {
+      copy = (EvalNode) origin.clone();
+    } catch (CloneNotSupportedException e) {
+      throw new PlanningException(e);
+    }
+    Set<Column> columns = EvalTreeUtil.findUniqueColumns(copy);
+    for (Column c: columns) {
+      Target target = targetMap.get(c.getQualifiedName());
+      if (target == null) {
+        throw new PlanningException(
+            "Invalid Filter PushDown: No such a corresponding target '"
+                + c.getQualifiedName() + " for FilterPushDown(" + origin + "), " +
+                "(PID=" + node.getPID() + ")"
+        );
+      }
+      EvalNode targetEvalNode = target.getEvalTree();
+      if (targetEvalNode.getType() != EvalType.FIELD) {
+        throw new PlanningException(
+            "Invalid Filter PushDown: '" + c.getQualifiedName() + "' target is not FieldEval " +
+                "(PID=" + node.getPID() + ")"
+        );
+      }
+
+      FieldEval fieldEval = (FieldEval)targetEvalNode;
+      Column targetInputColumn = fieldEval.getColumnRef();
+
+      int index;
+      if (targetInputColumn.hasQualifier()) {
+        index = node.getInSchema().getColumnId(targetInputColumn.getQualifiedName());
+      } else {
+        index = node.getInSchema().getColumnIdByName(targetInputColumn.getQualifiedName());
+      }
+      if (columnOffset > 0) {
+        index = index - columnOffset;
+      }
+      if (index < 0 || index >= outputSchema.size()) {
+        return null;
+      }
+      Column outputColumn = outputSchema.getColumn(index);
+
+      EvalTreeUtil.changeColumnRef(copy, c.getQualifiedName(), outputColumn.getQualifiedName());
+    }
+
+    return copy;
+  }
+
+  /**
+   * Find aggregation columns in filter eval and add having clause or add HavingNode.
+   * @param context
+   * @param plan
+   * @param block
+   * @param parentNode  If null, having is parent
+   * @param havingNode      If null, projection is parent
+   * @param groupByNode
+   * @return matched origin eval
+   * @throws PlanningException
+   */
+  private List<EvalNode> addHavingNode(FilterPushDownContext context, LogicalPlan plan,
+                                       LogicalPlan.QueryBlock block,
+                                       UnaryNode parentNode,
+                                       HavingNode havingNode,
+                                       GroupbyNode groupByNode) throws PlanningException {
+    // find aggregation column
+    Set<Column> groupingColumns = new HashSet<Column>(Arrays.asList(groupByNode.getGroupingColumns()));
+    Set<String> aggrFunctionOutColumns = new HashSet<String>();
+    for (Column column : groupByNode.getOutSchema().getColumns()) {
+      if (!groupingColumns.contains(column)) {
+        aggrFunctionOutColumns.add(column.getQualifiedName());
+      }
+    }
+
+    List<EvalNode> aggrEvalOrigins = new ArrayList<EvalNode>();
+    List<EvalNode> aggrEvals = new ArrayList<EvalNode>();
+
+    for (EvalNode eval : context.pushingDownFilters) {
+      EvalNode copy = null;
+      try {
+        copy = (EvalNode)eval.clone();
+      } catch (CloneNotSupportedException e) {
+      }
+      boolean isEvalAggrFunction = false;
+      for (Column evalColumn : EvalTreeUtil.findUniqueColumns(copy)) {
+        if (aggrFunctionOutColumns.contains(evalColumn.getSimpleName())) {
+          EvalTreeUtil.changeColumnRef(copy, evalColumn.getQualifiedName(), evalColumn.getSimpleName());
+          isEvalAggrFunction = true;
+          break;
+        }
+      }
+      if (isEvalAggrFunction) {
+        aggrEvals.add(copy);
+        aggrEvalOrigins.add(eval);
+      }
+    }
+
+    if (aggrEvals.isEmpty()) {
+      return aggrEvalOrigins;
+    }
+
+    // transform
+
+    HavingNode workingHavingNode;
+    if (havingNode != null) {
+      workingHavingNode = havingNode;
+      aggrEvals.add(havingNode.getQual());
+    } else {
+      workingHavingNode = plan.createNode(HavingNode.class);
+      block.registerNode(workingHavingNode);
+      parentNode.setChild(workingHavingNode);
+      workingHavingNode.setChild(groupByNode);
+    }
+
+    EvalNode qual = null;
+    if (aggrEvals.size() > 1) {
+      // merged into one eval tree
+      qual = AlgebraicUtil.createSingletonExprFromCNF(aggrEvals.toArray(new EvalNode[aggrEvals.size()]));
+    } else if (aggrEvals.size() == 1) {
+      // if the number of matched expr is one
+      qual = aggrEvals.get(0);
+    }
+
+    // If there is not matched node add SelectionNode and clear context.pushingDownFilters
+    if (qual != null) {
+      workingHavingNode.setQual(qual);
+    }
+
+    return aggrEvalOrigins;
+  }
+
+  @Override
+  public LogicalNode visitGroupBy(FilterPushDownContext context, LogicalPlan plan,
+                                  LogicalPlan.QueryBlock block, GroupbyNode groupbyNode,
+                                  Stack<LogicalNode> stack) throws PlanningException {
+    LogicalNode parentNode = stack.peek();
+    List<EvalNode> aggrEvals;
+    if (parentNode.getType() == NodeType.HAVING) {
+      aggrEvals = addHavingNode(context, plan, block, null, (HavingNode)parentNode, groupbyNode);
+    } else {
+      aggrEvals = addHavingNode(context, plan, block, (UnaryNode)parentNode, null, groupbyNode);
+    }
+
+    if (aggrEvals != null) {
+      // remove aggregation eval from conext
+      context.pushingDownFilters.removeAll(aggrEvals);
+    }
+
+    List<EvalNode> notMatched = new ArrayList<EvalNode>();
+    // transform
+    Map<EvalNode, EvalNode> tranformed =
+        findCanPushdownAndTransform(context, groupbyNode,groupbyNode.getChild(), notMatched, false, 0);
+
+    context.setFiltersTobePushed(tranformed.keySet());
+    LogicalNode current = super.visitGroupBy(context, plan, block, groupbyNode, stack);
+
+    context.setToOrigin(tranformed);
+    context.addFiltersTobePushed(notMatched);
+
+    return current;
+  }
+
+  @Override
+  public LogicalNode visitScan(FilterPushDownContext context, LogicalPlan plan,
+                               LogicalPlan.QueryBlock block, ScanNode scanNode,
                                Stack<LogicalNode> stack) throws PlanningException {
     List<EvalNode> matched = Lists.newArrayList();
-    for (EvalNode eval : cnf) {
+
+    // find partition column and check matching
+    Set<String> partitionColumns = new HashSet<String>();
+    TableDesc table = scanNode.getTableDesc();
+    if (table.hasPartition()) {
+      for (Column c: table.getPartitionMethod().getExpressionSchema().getColumns()) {
+        partitionColumns.add(c.getQualifiedName());
+      }
+    }
+    Set<EvalNode> partitionEvals = new HashSet<EvalNode>();
+    for (EvalNode eval : context.pushingDownFilters) {
+      if (table.hasPartition()) {
+        Set<Column> columns = EvalTreeUtil.findUniqueColumns(eval);
+        if (columns.size() != 1) {
+          continue;
+        }
+        Column column = columns.iterator().next();
+
+        if (partitionColumns.contains(column.getSimpleName())) {
+          EvalNode copy;
+          try {
+            copy = (EvalNode) eval.clone();
+          } catch (CloneNotSupportedException e) {
+            throw new PlanningException(e);
+          }
+          EvalTreeUtil.changeColumnRef(copy, column.getQualifiedName(),
+              scanNode.getCanonicalName() + "." + column.getSimpleName());
+          matched.add(copy);
+          partitionEvals.add(eval);
+        }
+      }
+    }
+
+    context.pushingDownFilters.removeAll(partitionEvals);
+
+    List<EvalNode> notMatched = new ArrayList<EvalNode>();
+
+    // transform
+    Map<EvalNode, EvalNode> transformed =
+        findCanPushdownAndTransform(context, scanNode, null, notMatched, true, 0);
+
+    for (EvalNode eval : transformed.keySet()) {
       if (LogicalPlanner.checkIfBeEvaluatedAtRelation(block, eval, scanNode)) {
         matched.add(eval);
       }
@@ -321,15 +795,33 @@ public class FilterPushDownRule extends BasicLogicalPlanVisitor<Set<EvalNode>, L
           matched.toArray(new EvalNode[matched.size()]));
     } else if (matched.size() == 1) {
       // if the number of matched expr is one
-      qual = matched.get(0);
+      qual = matched.iterator().next();
     }
 
     if (qual != null) { // if a matched qual exists
       scanNode.setQual(qual);
     }
 
-    cnf.removeAll(matched);
+    for (EvalNode matchedEval: matched) {
+      transformed.remove(matchedEval);
+    }
+
+    context.setToOrigin(transformed);
+    context.addFiltersTobePushed(notMatched);
 
     return scanNode;
   }
+
+  private void errorFilterPushDown(LogicalPlan plan, LogicalNode node,
+                                   FilterPushDownContext context) throws PlanningException {
+    String notMatchedNodeStr = "";
+    String prefix = "";
+    for (EvalNode notMatchedNode: context.pushingDownFilters) {
+      notMatchedNodeStr += prefix + notMatchedNode;
+      prefix = ", ";
+    }
+    throw new PlanningException("FilterPushDown failed cause some filters not matched: " + notMatchedNodeStr + "\n" +
+        "Error node: " + node.getPlanString() + "\n" +
+        plan.toString());
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/java/org/apache/tajo/engine/query/TestUnionQuery.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestUnionQuery.java b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestUnionQuery.java
index a54f670..5845ba8 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestUnionQuery.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestUnionQuery.java
@@ -133,6 +133,59 @@ public class TestUnionQuery extends QueryTestCaseBase {
   }
 
   @Test
+  public final void testUnion11() throws Exception {
+    // test filter pushdown
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testUnion12() throws Exception {
+    // test filter pushdown
+    // with subquery in union query
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testUnion13() throws Exception {
+    // test filter pushdown
+    // with subquery in union query
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testUnion14() throws Exception {
+    // test filter pushdown
+    // with group by subquery in union query
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testUnion15() throws Exception {
+    // test filter pushdown
+    // with group by out of union query and join in union query
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testUnion16() throws Exception {
+    // test filter pushdown
+    // with count distinct out of union query and join in union query
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
   public final void testUnionWithSameAliasNames() throws Exception {
     ResultSet res = executeQuery();
     assertResultSet(res);

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinCoReferredEvalsFilterPushdown.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinCoReferredEvalsFilterPushdown.sql b/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinCoReferredEvalsFilterPushdown.sql
new file mode 100644
index 0000000..680311d
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinCoReferredEvalsFilterPushdown.sql
@@ -0,0 +1,13 @@
+select * from (
+select
+  r_regionkey,
+  n_regionkey,
+  (r_regionkey + n_regionkey) as plus
+from
+  region,
+  nation
+where
+  r_regionkey = n_regionkey
+order by
+  r_regionkey, n_regionkey
+) where plus > 10
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestSelectQuery/testWhereCond2.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestSelectQuery/testWhereCond2.sql b/tajo-core/src/test/resources/queries/TestSelectQuery/testWhereCond2.sql
index ff50369..7fa0d18 100644
--- a/tajo-core/src/test/resources/queries/TestSelectQuery/testWhereCond2.sql
+++ b/tajo-core/src/test/resources/queries/TestSelectQuery/testWhereCond2.sql
@@ -1,8 +1,9 @@
 select *
 from (
- select a.l_orderkey, count(*) as cnt
+ select a.l_orderkey, count(*) as cnt, sum(l_extendedprice) as sum1
   from lineitem a
   group by a.l_orderkey
+  having sum1 > 70000
 ) t
-where t.cnt > 0
+where t.cnt > 1
 order by t.l_orderkey
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion11.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion11.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion11.sql
new file mode 100644
index 0000000..ec1a430
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion11.sql
@@ -0,0 +1,8 @@
+select col1, col2, col3
+from (
+    select L_RETURNFLAG as col1, L_EXTENDEDPRICE as col2, concat(L_RECEIPTDATE, L_LINESTATUS) as col3 from lineitem
+    union all
+    select P_TYPE as col1, P_RETAILPRICE col2, P_NAME col3 from part
+) a
+where col3 like '1993%' and col2 > 46796
+

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion12.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion12.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion12.sql
new file mode 100644
index 0000000..6b6a9ad
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion12.sql
@@ -0,0 +1,13 @@
+select col1, col2, col3
+from (
+    select
+        col1, col2, col3
+    from
+        (select
+            L_RETURNFLAG as col1, L_EXTENDEDPRICE as col2, concat(L_RECEIPTDATE, L_LINESTATUS) as col3
+        from
+            lineitem) b
+    union all
+    select P_TYPE as col1, P_RETAILPRICE * 100 col2, concat('1993', P_NAME) col3 from part
+) a
+where col3 like '1993%' and col2 > 46796

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion13.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion13.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion13.sql
new file mode 100644
index 0000000..70b0891
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion13.sql
@@ -0,0 +1,14 @@
+select col1, col2, col3
+from (
+    select
+        col1, col2, col3
+    from
+        (select
+            L_RETURNFLAG as col1, L_EXTENDEDPRICE as col2, concat(L_RECEIPTDATE, L_LINESTATUS) as col3
+        from
+            lineitem
+        where col2 > 46796) b
+    union all
+    select P_TYPE as col1, P_RETAILPRICE * 100 col2, concat('1993', P_NAME) col3 from part
+) a
+where col3 like '1993%'

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion14.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion14.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion14.sql
new file mode 100644
index 0000000..f47510e
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion14.sql
@@ -0,0 +1,8 @@
+select col1, cnt
+from (
+    select L_RETURNFLAG as col1, count(*) as cnt from lineitem group by col1
+    union all
+    select cast(n_regionkey as TEXT) as col1, count(*) as cnt from nation group by col1
+) a
+where a.cnt > 1
+order by a.col1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion15.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion15.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion15.sql
new file mode 100644
index 0000000..2e382d0
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion15.sql
@@ -0,0 +1,15 @@
+select col1, sum(cnt)
+from (
+    select col1, cnt
+    from (  select l_returnflag col1, count(*) cnt from lineitem
+            join orders on l_orderkey = o_orderkey and o_custkey > 0
+            group by l_returnflag) b
+    where col1 = 'N'
+    union all
+    select cast(n_regionkey as TEXT) as col1, count(*) as cnt from nation
+    where n_regionkey > 2
+    group by col1
+) a
+where round(cast(a.cnt as FLOAT4)) > 1.0
+group by a.col1
+order by a.col1

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion16.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion16.sql b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion16.sql
new file mode 100644
index 0000000..59e9c1f
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestUnionQuery/testUnion16.sql
@@ -0,0 +1,15 @@
+select col1, sum(cnt)
+from (
+    select col1, cnt
+    from (  select l_returnflag col1, count(distinct l_orderkey) cnt from lineitem
+            join orders on l_orderkey = o_orderkey and o_custkey > 0
+            group by l_returnflag) b
+    where col1 = 'N'
+    union all
+    select cast(n_regionkey as TEXT) as col1, count(*) as cnt from nation
+    where n_regionkey > 2
+    group by col1
+) a
+where round(cast(a.cnt as FLOAT4)) > 1.0
+group by a.col1
+order by a.col1

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestSelectQuery/testWhereCond2.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testWhereCond2.result b/tajo-core/src/test/resources/results/TestSelectQuery/testWhereCond2.result
index 32c93ed..c2bee17 100644
--- a/tajo-core/src/test/resources/results/TestSelectQuery/testWhereCond2.result
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testWhereCond2.result
@@ -1,5 +1,3 @@
-l_orderkey,cnt
+l_orderkey,cnt,sum1
 -------------------------------
-1,2
-2,1
-3,2
\ No newline at end of file
+3,2,100854.52
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion11.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion11.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion11.result
new file mode 100644
index 0000000..6e8d2cd
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion11.result
@@ -0,0 +1,3 @@
+col1,col2,col3
+-------------------------------
+R,46796.47,1993-11-24F
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion12.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion12.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion12.result
new file mode 100644
index 0000000..c130afa
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion12.result
@@ -0,0 +1,6 @@
+col1,col2,col3
+-------------------------------
+R,46796.47,1993-11-24F
+PROMO BURNISHED COPPER,90100.0,1993goldenrod lavender spring chocolate lace
+LARGE BRUSHED BRASS,90200.0,1993blush thistle blue yellow saddle
+STANDARD POLISHED BRASS,90300.0,1993spring green yellow purple cornsilk
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion13.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion13.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion13.result
new file mode 100644
index 0000000..c130afa
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion13.result
@@ -0,0 +1,6 @@
+col1,col2,col3
+-------------------------------
+R,46796.47,1993-11-24F
+PROMO BURNISHED COPPER,90100.0,1993goldenrod lavender spring chocolate lace
+LARGE BRUSHED BRASS,90200.0,1993blush thistle blue yellow saddle
+STANDARD POLISHED BRASS,90300.0,1993spring green yellow purple cornsilk
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion14.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion14.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion14.result
new file mode 100644
index 0000000..3838ab4
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion14.result
@@ -0,0 +1,9 @@
+col1,cnt
+-------------------------------
+0,5
+1,5
+2,5
+3,5
+4,5
+N,3
+R,2
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion15.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion15.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion15.result
new file mode 100644
index 0000000..4e4d9e9
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion15.result
@@ -0,0 +1,5 @@
+col1,?sum
+-------------------------------
+3,5
+4,5
+N,3
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/e2d04647/tajo-core/src/test/resources/results/TestUnionQuery/testUnion16.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestUnionQuery/testUnion16.result b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion16.result
new file mode 100644
index 0000000..38be978
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestUnionQuery/testUnion16.result
@@ -0,0 +1,5 @@
+col1,?sum
+-------------------------------
+3,5
+4,5
+N,2
\ No newline at end of file


Mime
View raw message