You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/01/17 10:23:35 UTC

[05/12] TAJO-501: Rewrite the projection part of logical planning.

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/NamedExprsManager.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/NamedExprsManager.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/NamedExprsManager.java
new file mode 100644
index 0000000..05ba036
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/NamedExprsManager.java
@@ -0,0 +1,271 @@
+/**
+ * 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.tajo.engine.planner;
+
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+import org.apache.tajo.algebra.ColumnReferenceExpr;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.algebra.NamedExpr;
+import org.apache.tajo.algebra.OpType;
+import org.apache.tajo.annotation.Nullable;
+import org.apache.tajo.engine.eval.EvalNode;
+import org.apache.tajo.engine.eval.FieldEval;
+import org.apache.tajo.util.TUtil;
+
+import java.util.*;
+
+import static java.util.Map.Entry;
+
+/**
+ * NamedExprsManager manages an expressions to be evaluated in a query block.
+ * NamedExprsManager uses a reference name to identify one expression or one
+ * EvalNode (annotated expression).
+ */
+public class NamedExprsManager {
+  /** Map; Reference name -> EvalNode */
+  private Map<String, EvalNode> nameToEvalMap = new LinkedHashMap<String, EvalNode>();
+  /** Map: EvalNode -> String */
+  private Map<EvalNode, String> evalToNameMap = new LinkedHashMap<EvalNode, String>();
+  /** Map; Reference name -> Expr */
+  private LinkedHashMap<String, Expr> nameToExprMap = new LinkedHashMap<String, Expr>();
+  /** Map; Expr -> Reference Name */
+  private LinkedHashMap<Expr, String> exprToNameMap = new LinkedHashMap<Expr, String>();
+  /** Map; Reference Name -> Boolean (if it is resolved or not) */
+  private LinkedHashMap<String, Boolean> resolvedFlags = new LinkedHashMap<String, Boolean>();
+
+  private BiMap<String, String> aliasedColumnMap = HashBiMap.create();
+
+  private LogicalPlan plan;
+
+  public NamedExprsManager(LogicalPlan plan) {
+    this.plan = plan;
+  }
+
+  /**
+   * Check whether the expression corresponding to a given name was resolved.
+   *
+   * @param name The name of a certain expression to be checked
+   * @return true if resolved. Otherwise, false.
+   */
+  public boolean isResolved(String name) {
+    String normalized = name.toLowerCase();
+    return resolvedFlags.containsKey(normalized) && resolvedFlags.get(normalized);
+  }
+
+  public boolean contains(String name) {
+    return nameToExprMap.containsKey(name);
+  }
+
+  public boolean contains(Expr expr) {
+    return exprToNameMap.containsKey(expr);
+  }
+
+  public String getName(Expr expr) {
+    return exprToNameMap.get(expr);
+  }
+
+  public NamedExpr getNamedExpr(String name) {
+    String normalized = name.toLowerCase();
+    return new NamedExpr(nameToExprMap.get(normalized), normalized);
+  }
+
+  public boolean isAliased(String name) {
+    return aliasedColumnMap.containsKey(name);
+  }
+
+  public String getAlias(String originalName) {
+    return aliasedColumnMap.get(originalName);
+  }
+
+  public boolean isAliasedName(String aliasName) {
+    return aliasedColumnMap.inverse().containsKey(aliasName);
+  }
+
+  public String getOriginalName(String aliasName) {
+    return aliasedColumnMap.inverse().get(aliasName);
+  }
+
+  public String addExpr(Expr expr, String alias) {
+    if (exprToNameMap.containsKey(expr)) {
+      return exprToNameMap.get(expr);
+    } else {
+      String normalized = alias.toLowerCase();
+      nameToExprMap.put(normalized, expr);
+      exprToNameMap.put(expr, normalized);
+      resolvedFlags.put(normalized, false);
+      return normalized;
+    }
+  }
+
+  public String [] addReferences(Expr expr) throws PlanningException {
+    Set<ColumnReferenceExpr> foundSet = ExprFinder.finds(expr, OpType.Column);
+    String [] names = new String[foundSet.size()];
+    int i = 0;
+    for (ColumnReferenceExpr column : foundSet) {
+      addExpr(column);
+      names[i++] = column.getCanonicalName();
+    }
+    return names;
+  }
+
+  public String addExpr(Expr expr) {
+    String name;
+
+    // all columns are projected automatically. BTW, should we add column reference to this list?
+    if (expr.getType() == OpType.Column) {
+      name = ((ColumnReferenceExpr)expr).getCanonicalName();
+      if (nameToExprMap.containsKey(name)) { // if it is column and another one already exists, skip.
+        return name;
+      }
+    } else {
+      name = plan.newGeneratedFieldName(expr);
+    }
+    return addExpr(expr, name);
+  }
+
+  public String addNamedExpr(NamedExpr namedExpr) {
+    if (namedExpr.hasAlias()) {
+      return addExpr(namedExpr.getExpr(), namedExpr.getAlias());
+    } else {
+      return addExpr(namedExpr.getExpr());
+    }
+  }
+
+  public String [] addNamedExprArray(@Nullable Collection<NamedExpr> targets) {
+    if (targets != null || targets.size() > 0) {
+      String [] names = new String[targets.size()];
+      int i = 0;
+      for (NamedExpr target : targets) {
+        names[i++] = addNamedExpr(target);
+      }
+      return names;
+    } else {
+      return null;
+    }
+  }
+
+  public Collection<NamedExpr> getAllNamedExprs() {
+    List<NamedExpr> namedExprList = new ArrayList<NamedExpr>();
+    for (Entry<String, Expr> entry: nameToExprMap.entrySet()) {
+      namedExprList.add(new NamedExpr(entry.getValue(), entry.getKey()));
+    }
+    return namedExprList;
+  }
+
+  public void resolveExpr(String name, EvalNode evalNode) throws PlanningException {
+    String normalized = name.toLowerCase();
+    nameToEvalMap.put(normalized, evalNode);
+    evalToNameMap.put(evalNode, normalized);
+    resolvedFlags.put(normalized, true);
+
+    String originalName = checkAndGetIfAliasedColumn(normalized);
+    if (originalName != null) {
+      aliasedColumnMap.put(originalName, normalized);
+    }
+  }
+
+  /**
+   * It returns an original column name if it is aliased column reference.
+   * Otherwise, it will return NULL.
+   */
+  private String checkAndGetIfAliasedColumn(String name) {
+    Expr expr = nameToExprMap.get(name);
+    if (expr.getType() == OpType.Column) {
+      ColumnReferenceExpr column = (ColumnReferenceExpr) expr;
+      if (!column.getCanonicalName().equals(name)) {
+        return column.getCanonicalName();
+      }
+    }
+    return null;
+  }
+
+  public Target getTarget(Expr expr, boolean unresolved) {
+    String name = exprToNameMap.get(expr);
+    return getTarget(name, unresolved);
+  }
+
+  public Target getTarget(String name) {
+    return getTarget(name, false);
+  }
+
+  public Target getTarget(String name, boolean unresolved) {
+    String normalized = name;
+    if (!unresolved && resolvedFlags.containsKey(normalized) && resolvedFlags.get(normalized)) {
+      return new Target(new FieldEval(normalized, nameToEvalMap.get(normalized).getValueType()));
+    } else {
+      if (nameToEvalMap.containsKey(normalized)) {
+        return new Target(nameToEvalMap.get(normalized), name);
+      } else {
+        return null;
+      }
+    }
+  }
+
+  public String toString() {
+    return "unresolved=" + nameToExprMap.size() + ", resolved=" + nameToEvalMap.size()
+        + ", renamed=" + aliasedColumnMap.size();
+  }
+
+  /**
+   * It returns an iterator for unresolved NamedExprs.
+   */
+  public Iterator<NamedExpr> getUnresolvedExprs() {
+    return new UnresolvedIterator();
+  }
+
+  public class UnresolvedIterator implements Iterator<NamedExpr> {
+    private final Iterator<NamedExpr> iterator;
+
+    public UnresolvedIterator() {
+      List<NamedExpr> unresolvedList = TUtil.newList();
+      for (Entry<String,Expr> entry : nameToExprMap.entrySet()) {
+        if (!isResolved(entry.getKey())) {
+          unresolvedList.add(new NamedExpr(entry.getValue(), entry.getKey()));
+        }
+      }
+      if (unresolvedList.size() == 0) {
+        iterator = null;
+      } else {
+        iterator = unresolvedList.iterator();
+      }
+    }
+
+
+    @Override
+    public boolean hasNext() {
+      return iterator != null && iterator.hasNext();
+    }
+
+    @Override
+    public NamedExpr next() {
+      return iterator.next();
+    }
+
+    @Override
+    public void remove() {
+    }
+  }
+
+  public void reset() {
+    for (String name : resolvedFlags.keySet()) {
+      resolvedFlags.put(name, false);
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
index d6d518c..d9689aa 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
@@ -26,6 +26,7 @@ import com.google.common.collect.ObjectArrays;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.fs.Path;
+import org.apache.tajo.algebra.Projection;
 import org.apache.tajo.engine.planner.global.DataChannel;
 import org.apache.tajo.storage.fragment.FileFragment;
 import org.apache.tajo.storage.fragment.FragmentConvertor;
@@ -133,7 +134,8 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
       case TABLE_SUBQUERY: {
         TableSubQueryNode subQueryNode = (TableSubQueryNode) logicalNode;
         leftExec = createPlanRecursive(ctx, subQueryNode.getSubQuery());
-        return leftExec;
+        ProjectionExec projectionExec = new ProjectionExec(ctx, subQueryNode, leftExec);
+        return projectionExec;
 
       }
       case PARTITIONS_SCAN:
@@ -146,6 +148,11 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
         leftExec = createPlanRecursive(ctx, grpNode.getChild());
         return createGroupByPlan(ctx, grpNode, leftExec);
 
+      case HAVING:
+        HavingNode havingNode = (HavingNode) logicalNode;
+        leftExec = createPlanRecursive(ctx, havingNode.getChild());
+        return new HavingExec(ctx, havingNode, leftExec);
+
       case SORT:
         SortNode sortNode = (SortNode) logicalNode;
         leftExec = createPlanRecursive(ctx, sortNode.getChild());
@@ -327,12 +334,18 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
                                              PhysicalExec leftExec, PhysicalExec rightExec) throws IOException {
     SortSpec[][] sortSpecs = PlannerUtil.getSortKeysFromJoinQual(
         plan.getJoinQual(), leftExec.getSchema(), rightExec.getSchema());
-    ExternalSortExec outerSort = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID, sortSpecs[0], leftExec.getSchema(), leftExec.getSchema()),
-        leftExec);
-    ExternalSortExec innerSort = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID, sortSpecs[1], rightExec.getSchema(), rightExec.getSchema()),
-        rightExec);
+
+    SortNode leftSortNode = new SortNode(UNGENERATED_PID);
+    leftSortNode.setSortSpecs(sortSpecs[0]);
+    leftSortNode.setInSchema(leftExec.getSchema());
+    leftSortNode.setOutSchema(leftExec.getSchema());
+    ExternalSortExec outerSort = new ExternalSortExec(context, sm, leftSortNode, leftExec);
+
+    SortNode rightSortNode = new SortNode(UNGENERATED_PID);
+    rightSortNode.setSortSpecs(sortSpecs[1]);
+    rightSortNode.setInSchema(rightExec.getSchema());
+    rightSortNode.setOutSchema(rightExec.getSchema());
+    ExternalSortExec innerSort = new ExternalSortExec(context, sm, rightSortNode, rightExec);
 
     LOG.info("Join (" + plan.getPID() +") chooses [Merge Join]");
     return new MergeJoinExec(context, plan, outerSort, innerSort, sortSpecs[0], sortSpecs[1]);
@@ -399,10 +412,19 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
     LOG.info("Right Outer Join (" + plan.getPID() +") chooses [Merge Join].");
     SortSpec[][] sortSpecs2 = PlannerUtil.getSortKeysFromJoinQual(
         plan.getJoinQual(), leftExec.getSchema(), rightExec.getSchema());
-    ExternalSortExec outerSort2 = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID,sortSpecs2[0], leftExec.getSchema(), leftExec.getSchema()), leftExec);
-    ExternalSortExec innerSort2 = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID,sortSpecs2[1], rightExec.getSchema(), rightExec.getSchema()), rightExec);
+
+    SortNode leftSortNode2 = new SortNode(UNGENERATED_PID);
+    leftSortNode2.setSortSpecs(sortSpecs2[0]);
+    leftSortNode2.setInSchema(leftExec.getSchema());
+    leftSortNode2.setOutSchema(leftExec.getSchema());
+    ExternalSortExec outerSort2 = new ExternalSortExec(context, sm, leftSortNode2, leftExec);
+
+    SortNode rightSortNode2 = new SortNode(UNGENERATED_PID);
+    rightSortNode2.setSortSpecs(sortSpecs2[1]);
+    rightSortNode2.setInSchema(rightExec.getSchema());
+    rightSortNode2.setOutSchema(rightExec.getSchema());
+    ExternalSortExec innerSort2 = new ExternalSortExec(context, sm, rightSortNode2, rightExec);
+
     return new RightOuterMergeJoinExec(context, plan, outerSort2, innerSort2, sortSpecs2[0], sortSpecs2[1]);
   }
 
@@ -481,10 +503,18 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
     LOG.info("Full Outer Join (" + plan.getPID() +") chooses [Merge Join]");
     SortSpec[][] sortSpecs3 = PlannerUtil.getSortKeysFromJoinQual(plan.getJoinQual(),
         leftExec.getSchema(), rightExec.getSchema());
-    ExternalSortExec outerSort3 = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID,sortSpecs3[0], leftExec.getSchema(), leftExec.getSchema()), leftExec);
-    ExternalSortExec innerSort3 = new ExternalSortExec(context, sm,
-        new SortNode(UNGENERATED_PID,sortSpecs3[1], rightExec.getSchema(), rightExec.getSchema()), rightExec);
+
+    SortNode leftSortNode = new SortNode(UNGENERATED_PID);
+    leftSortNode.setSortSpecs(sortSpecs3[0]);
+    leftSortNode.setInSchema(leftExec.getSchema());
+    leftSortNode.setOutSchema(leftExec.getSchema());
+    ExternalSortExec outerSort3 = new ExternalSortExec(context, sm, leftSortNode, leftExec);
+
+    SortNode rightSortNode = new SortNode(UNGENERATED_PID);
+    rightSortNode.setSortSpecs(sortSpecs3[1]);
+    rightSortNode.setInSchema(rightExec.getSchema());
+    rightSortNode.setOutSchema(rightExec.getSchema());
+    ExternalSortExec innerSort3 = new ExternalSortExec(context, sm, rightSortNode, rightExec);
 
     return new MergeFullOuterJoinExec(context, plan, outerSort3, innerSort3, sortSpecs3[0], sortSpecs3[1]);
   }
@@ -706,7 +736,8 @@ public class PhysicalPlannerImpl implements PhysicalPlanner {
       sortSpecs = ObjectArrays.concat(sortSpecs, enforcedSortSpecs, SortSpec.class);
     }
 
-    SortNode sortNode = new SortNode(-1, sortSpecs);
+    SortNode sortNode = new SortNode(-1);
+    sortNode.setSortSpecs(sortSpecs);
     sortNode.setInSchema(subOp.getSchema());
     sortNode.setOutSchema(subOp.getSchema());
     // SortExec sortExec = new SortExec(sortNode, child);

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
index 4e2441b..66358d5 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
@@ -22,7 +22,7 @@ import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 import com.google.common.collect.ObjectArrays;
 import com.google.common.collect.Sets;
-import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.algebra.*;
 import org.apache.tajo.annotation.Nullable;
 import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.Schema;
@@ -33,6 +33,7 @@ import org.apache.tajo.engine.eval.*;
 import org.apache.tajo.engine.planner.logical.*;
 import org.apache.tajo.engine.exception.InvalidQueryException;
 import org.apache.tajo.storage.TupleComparator;
+import org.apache.tajo.util.TUtil;
 
 import java.util.*;
 
@@ -139,12 +140,17 @@ public class PlannerUtil {
   public static void replaceNode(LogicalPlan plan, LogicalNode startNode, LogicalNode oldNode, LogicalNode newNode) {
     LogicalNodeReplaceVisitor replacer = new LogicalNodeReplaceVisitor(oldNode, newNode);
     try {
-      replacer.visit(null, plan, null, startNode, new Stack<LogicalNode>());
+      replacer.visit(new ReplacerContext(), plan, null, startNode, new Stack<LogicalNode>());
     } catch (PlanningException e) {
       e.printStackTrace();
     }
   }
-  public static class LogicalNodeReplaceVisitor extends BasicLogicalPlanVisitor<Object, LogicalNode> {
+
+  static class ReplacerContext {
+    boolean updateSchemaFlag = false;
+  }
+
+  public static class LogicalNodeReplaceVisitor extends BasicLogicalPlanVisitor<ReplacerContext, LogicalNode> {
     private LogicalNode target;
     private LogicalNode tobeReplaced;
 
@@ -154,9 +160,9 @@ public class PlannerUtil {
     }
 
     @Override
-    public LogicalNode visit(Object context, LogicalPlan plan, @Nullable LogicalPlan.QueryBlock block, LogicalNode node,
-                                  Stack<LogicalNode> stack) throws PlanningException {
-      super.visit(context, plan, null, node, stack);
+    public LogicalNode visit(ReplacerContext context, LogicalPlan plan, @Nullable LogicalPlan.QueryBlock block,
+                             LogicalNode node, Stack<LogicalNode> stack) throws PlanningException {
+      LogicalNode child = super.visit(context, plan, null, node, stack);
 
       if (node.deepEquals(target)) {
         LogicalNode parent = stack.peek();
@@ -173,6 +179,18 @@ public class PlannerUtil {
           UnaryNode unaryParent = (UnaryNode) parent;
           unaryParent.setChild(tobeReplaced);
         }
+
+        context.updateSchemaFlag = true;
+      }
+
+      if (context.updateSchemaFlag && !node.deepEquals(target)) {
+        if (node instanceof Projectable) {
+          node.setInSchema(child.getOutSchema());
+          context.updateSchemaFlag = false;
+        } else {
+          node.setInSchema(child.getOutSchema());
+          node.setOutSchema(child.getOutSchema());
+        }
       }
       return node;
     }
@@ -241,10 +259,8 @@ public class PlannerUtil {
                 new Column(targetName, newTarget.getEvalTree().getValueType()))});
           } else {
             func.setFirstPhase();
-            newTarget = new Target(func);
             String targetName = "column_" + (targetId++);
-            newTarget.setAlias(targetName);
-
+            newTarget = new Target(func, targetName);
             AggregationFunctionCallEval secondFunc = null;
             for (AggregationFunctionCallEval sf : secondStepFunctions) {
               if (func.equals(sf)) {
@@ -264,14 +280,13 @@ public class PlannerUtil {
       Schema targetSchema = PlannerUtil.targetToSchema(targetArray);
       List<Target> newTarget = Lists.newArrayList();
       for (Column column : groupBy.getGroupingColumns()) {
-        if (!targetSchema.contains(column.getQualifiedName())) {
+        if (!targetSchema.containsByQualifiedName(column.getQualifiedName())) {
           newTarget.add(new Target(new FieldEval(column)));
         }
       }
       targetArray = ObjectArrays.concat(targetArray, newTarget.toArray(new Target[newTarget.size()]), Target.class);
 
       child.setTargets(targetArray);
-      child.setOutSchema(PlannerUtil.targetToSchema(targetArray));
       // set the groupby chaining
       groupBy.setChild(child);
       groupBy.setInSchema(child.getOutSchema());
@@ -409,7 +424,7 @@ public class PlannerUtil {
     } else {
 
       for (Column col : columnRefs) {
-        if (!node.getInSchema().contains(col.getQualifiedName())) {
+        if (!node.getInSchema().containsByQualifiedName(col.getQualifiedName())) {
           return false;
         }
       }
@@ -506,6 +521,21 @@ public class PlannerUtil {
     return targets;
   }
 
+  public static Target[] schemaToTargetsWithGeneratedFields(Schema schema) {
+    List<Target> targets = TUtil.newList();
+
+    FieldEval eval;
+    Column column;
+    for (int i = 0; i < schema.getColumnNum(); i++) {
+      column = schema.getColumn(i);
+      if (column.getColumnName().charAt(0) != LogicalPlan.NONAMED_COLUMN_PREFIX) {
+        eval = new FieldEval(schema.getColumn(i));
+        targets.add(new Target(eval));
+      }
+    }
+    return targets.toArray(new Target[targets.size()]);
+  }
+
   public static SortSpec[] schemaToSortSpecs(Schema schema) {
     return schemaToSortSpecs(schema.toArray());
   }
@@ -619,7 +649,7 @@ public class PlannerUtil {
           for (int j = 0; j < schemas.length; j++) {
           // check whether the column is for either outer or inner
           // 0 is outer, and 1 is inner
-            if (schemas[j].contains(column.getQualifiedName())) {
+            if (schemas[j].containsByQualifiedName(column.getQualifiedName())) {
               pair[j] = column;
             }
           }
@@ -637,6 +667,10 @@ public class PlannerUtil {
     }
   }
 
+  public static Schema targetToSchema(Collection<Target> targets) {
+    return targetToSchema(targets.toArray(new Target[targets.size()]));
+  }
+
   public static Schema targetToSchema(Target[] targets) {
     Schema schema = new Schema();
     for(Target t : targets) {
@@ -647,7 +681,9 @@ public class PlannerUtil {
       } else {
         name = t.getEvalTree().getName();
       }
-      schema.addColumn(name, type);
+      if (!schema.containsByQualifiedName(name)) {
+        schema.addColumn(name, type);
+      }
     }
 
     return schema;
@@ -668,7 +704,7 @@ public class PlannerUtil {
         throw new InternalError(e.getMessage());
       }
       if (copy[i].getEvalTree().getType() == EvalType.FIELD) {
-        FieldEval fieldEval = (FieldEval) copy[i].getEvalTree();
+        FieldEval fieldEval = copy[i].getEvalTree();
         if (fieldEval.getColumnRef().hasQualifier()) {
           fieldEval.getColumnRef().setName(fieldEval.getColumnName());
         }
@@ -706,4 +742,51 @@ public class PlannerUtil {
     return schema;
   }
 
+  public static boolean existsAggregationFunction(Expr expr) throws PlanningException {
+    AggregationFunctionFinder finder = new AggregationFunctionFinder();
+    AggFunctionFoundResult result = new AggFunctionFoundResult();
+    finder.visit(result, new Stack<Expr>(), expr);
+    return result.generalSetFunction;
+  }
+
+  public static boolean existsDistinctAggregationFunction(Expr expr) throws PlanningException {
+    AggregationFunctionFinder finder = new AggregationFunctionFinder();
+    AggFunctionFoundResult result = new AggFunctionFoundResult();
+    finder.visit(result, new Stack<Expr>(), expr);
+    return result.distinctSetFunction;
+  }
+
+  static class AggFunctionFoundResult {
+    boolean generalSetFunction;
+    boolean distinctSetFunction;
+  }
+  static class AggregationFunctionFinder extends SimpleAlgebraVisitor<AggFunctionFoundResult, Object> {
+    @Override
+    public Object visitCountRowsFunction(AggFunctionFoundResult ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
+        throws PlanningException {
+      ctx.generalSetFunction = true;
+      return super.visitCountRowsFunction(ctx, stack, expr);
+    }
+
+    @Override
+    public Object visitGeneralSetFunction(AggFunctionFoundResult ctx, Stack<Expr> stack, GeneralSetFunctionExpr expr)
+        throws PlanningException {
+      ctx.generalSetFunction = true;
+      ctx.distinctSetFunction = expr.isDistinct();
+      return super.visitGeneralSetFunction(ctx, stack, expr);
+    }
+  }
+
+  public static Collection<String> toQualifiedFieldNames(Collection<String> fieldNames, String qualifier) {
+    List<String> names = TUtil.newList();
+    for (String n : fieldNames) {
+      String [] parts = n.split("\\.");
+      if (parts.length == 1) {
+        names.add(qualifier + "." + parts[0]);
+      } else {
+        names.add(qualifier + "." + parts[1]);
+      }
+    }
+    return names;
+  }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Projector.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Projector.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Projector.java
index 12e4978..8d6db22 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Projector.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Projector.java
@@ -18,7 +18,6 @@
 
 package org.apache.tajo.engine.planner;
 
-import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.engine.eval.EvalContext;
 import org.apache.tajo.engine.eval.EvalNode;
@@ -29,59 +28,21 @@ public class Projector {
 
   // for projection
   private final int targetNum;
-  private final int [] inMap;
-  private final int [] outMap;
-  private int [] evalOutMap; // target list?
-  private EvalNode[] evals;
-  private Tuple prevTuple;
+  private final EvalNode[] evals;
 
   public Projector(Schema inSchema, Schema outSchema, Target [] targets) {
     this.inSchema = inSchema;
-
-    this.targetNum = targets != null ? targets.length : 0;
-
-    inMap = new int[outSchema.getColumnNum() - targetNum];
-    outMap = new int[outSchema.getColumnNum() - targetNum];
-    int mapId = 0;
-    Column col;
-
-    if (targetNum > 0) {
-      evalOutMap = new int[targetNum];
-      evals = new EvalNode[targetNum];
-      for (int i = 0; i < targetNum; i++) {
-        // TODO - is it always  correct?
-        if (targets[i].hasAlias()) {
-          evalOutMap[i] = outSchema.getColumnId(targets[i].getAlias());
-        } else {
-          evalOutMap[i] = outSchema.getColumnId(targets[i].getEvalTree().getName());
-        }
-        evals[i] = targets[i].getEvalTree();
-      }
-
-      outer:
-      for (int targetId = 0; targetId < outSchema.getColumnNum(); targetId ++) {
-        for (int j = 0; j < evalOutMap.length; j++) {
-          if (evalOutMap[j] == targetId)
-            continue outer;
-        }
-
-        col = inSchema.getColumnByFQN(outSchema.getColumn(targetId).getQualifiedName());
-        outMap[mapId] = targetId;
-        inMap[mapId] = inSchema.getColumnId(col.getQualifiedName());
-        mapId++;
-      }
-    } else {
-      for (int targetId = 0; targetId < outSchema.getColumnNum(); targetId ++) {
-        col = inSchema.getColumnByFQN(outSchema.getColumn(targetId).getQualifiedName());
-        outMap[mapId] = targetId;
-        inMap[mapId] = inSchema.getColumnId(col.getQualifiedName());
-        mapId++;
-      }
+    if (targets == null) {
+      targets = PlannerUtil.schemaToTargets(outSchema);
+    }
+    this.targetNum = targets.length;
+    evals = new EvalNode[targetNum];
+    for (int i = 0; i < targetNum; i++) {
+      evals[i] = targets[i].getEvalTree();
     }
   }
 
   public void eval(EvalContext[] evalContexts, Tuple in) {
-    this.prevTuple = in;
     if (targetNum > 0) {
       for (int i = 0; i < evals.length; i++) {
         evals[i].eval(evalContexts[i], inSchema, in);
@@ -90,17 +51,12 @@ public class Projector {
   }
 
   public void terminate(EvalContext [] evalContexts, Tuple out) {
-    for (int i = 0; i < inMap.length; i++) {
-      out.put(outMap[i], prevTuple.get(inMap[i]));
-    }
-    if (targetNum > 0) {
-      for (int i = 0; i < evals.length; i++) {
-        out.put(evalOutMap[i], evals[i].terminate(evalContexts[i]));
-      }
+    for (int i = 0; i < targetNum; i++) {
+      out.put(i, evals[i].terminate(evalContexts[i]));
     }
   }
 
-  public EvalContext [] renew() {
+  public EvalContext [] newContexts() {
     EvalContext [] evalContexts = new EvalContext[targetNum];
     for (int i = 0; i < targetNum; i++) {
       evalContexts[i] = evals[i].newContext();

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/RangePartitionAlgorithm.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/RangePartitionAlgorithm.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/RangePartitionAlgorithm.java
index e25903f..35d7743 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/RangePartitionAlgorithm.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/RangePartitionAlgorithm.java
@@ -60,6 +60,9 @@ public abstract class RangePartitionAlgorithm {
     BigDecimal columnCard;
 
     switch (dataType.getType()) {
+      case BOOLEAN:
+        columnCard = new BigDecimal(2);
+        break;
       case CHAR:
         columnCard = new BigDecimal(end.asChar() - start.asChar());
         break;

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
new file mode 100644
index 0000000..dc7b7a2
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
@@ -0,0 +1,210 @@
+/**
+ * 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.tajo.engine.planner;
+
+import org.apache.tajo.algebra.*;
+
+import java.util.Stack;
+
+/**
+ * <code>SimpleAlgebraVisitor</code> provides a simple and fewer visit methods. It makes building concrete class easier.
+ */
+public abstract class SimpleAlgebraVisitor<CONTEXT, RESULT> extends BaseAlgebraVisitor<CONTEXT, RESULT> {
+
+  public RESULT visit(CONTEXT ctx, Stack<Expr> stack, Expr expr) throws PlanningException {
+    RESULT result = null;
+    if (expr instanceof UnaryOperator) {
+      preHook(ctx, stack, expr);
+      result = visitUnaryOperator(ctx, stack, (UnaryOperator) expr);
+      postHook(ctx, stack, expr, result);
+    } else if (expr instanceof BinaryOperator) {
+      preHook(ctx, stack, expr);
+      result = visitBinaryOperator(ctx, stack, (BinaryOperator) expr);
+      postHook(ctx, stack, expr, result);
+    } else {
+      result = super.visit(ctx, stack, expr);
+    }
+
+    return result;
+  }
+
+  public RESULT visitUnaryOperator(CONTEXT ctx, Stack<Expr> stack, UnaryOperator expr) throws PlanningException {
+    stack.push(expr);
+    RESULT result = visit(ctx, stack, expr.getChild());
+    stack.pop();
+    return result;
+  }
+
+  public RESULT visitBinaryOperator(CONTEXT ctx, Stack<Expr> stack, BinaryOperator expr) throws PlanningException {
+    stack.push(expr);
+    visit(ctx, stack, expr.getLeft());
+    RESULT result = visit(ctx, stack, expr.getRight());
+    stack.pop();
+    return result;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Relational Operator Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitProjection(CONTEXT ctx, Stack<Expr> stack, Projection expr) throws PlanningException {
+    return super.visitProjection(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitLimit(CONTEXT ctx, Stack<Expr> stack, Limit expr) throws PlanningException {
+    return super.visitLimit(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitSort(CONTEXT ctx, Stack<Expr> stack, Sort expr) throws PlanningException {
+    return super.visitSort(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitHaving(CONTEXT ctx, Stack<Expr> stack, Having expr) throws PlanningException {
+    return super.visitHaving(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitGroupBy(CONTEXT ctx, Stack<Expr> stack, Aggregation expr) throws PlanningException {
+    return super.visitGroupBy(ctx, stack, expr);
+  }
+
+  public RESULT visitFilter(CONTEXT ctx, Stack<Expr> stack, Selection expr) throws PlanningException {
+    return super.visitFilter(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitJoin(CONTEXT ctx, Stack<Expr> stack, Join expr) throws PlanningException {
+    return super.visitJoin(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitTableSubQuery(CONTEXT ctx, Stack<Expr> stack, TablePrimarySubQuery expr) throws PlanningException {
+    return super.visitTableSubQuery(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitRelationList(CONTEXT ctx, Stack<Expr> stack, RelationList expr) throws PlanningException {
+    return super.visitRelationList(ctx, stack, expr);
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Data Definition Language Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitCreateTable(CONTEXT ctx, Stack<Expr> stack, CreateTable expr) throws PlanningException {
+    return super.visitCreateTable(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitDropTable(CONTEXT ctx, Stack<Expr> stack, DropTable expr) throws PlanningException {
+    return super.visitDropTable(ctx, stack, expr);
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Insert or Update Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  public RESULT visitInsert(CONTEXT ctx, Stack<Expr> stack, Insert expr) throws PlanningException {
+    return super.visitInsert(ctx, stack, expr);
+  }
+
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Other Predicates Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitBetween(CONTEXT ctx, Stack<Expr> stack, BetweenPredicate expr) throws PlanningException {
+    return super.visitBetween(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitCaseWhen(CONTEXT ctx, Stack<Expr> stack, CaseWhenPredicate expr) throws PlanningException {
+    return super.visitCaseWhen(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitValueListExpr(CONTEXT ctx, Stack<Expr> stack, ValueListExpr expr) throws PlanningException {
+    return super.visitValueListExpr(ctx, stack, expr);
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Other Expressions
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitFunction(CONTEXT ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
+    return super.visitFunction(ctx, stack, expr);
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // General Set Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitCountRowsFunction(CONTEXT ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
+      throws PlanningException {
+    return super.visitCountRowsFunction(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitGeneralSetFunction(CONTEXT ctx, Stack<Expr> stack, GeneralSetFunctionExpr expr)
+      throws PlanningException {
+    return super.visitGeneralSetFunction(ctx, stack, expr);
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Literal Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public RESULT visitDataType(CONTEXT ctx, Stack<Expr> stack, DataTypeExpr expr) throws PlanningException {
+    return super.visitDataType(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitCastExpr(CONTEXT ctx, Stack<Expr> stack, CastExpr expr) throws PlanningException {
+    return super.visitCastExpr(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitLiteral(CONTEXT ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException {
+    return super.visitLiteral(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitNullLiteral(CONTEXT ctx, Stack<Expr> stack, NullLiteral expr) throws PlanningException {
+    return super.visitNullLiteral(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitTimestampLiteral(CONTEXT ctx, Stack<Expr> stack, TimestampLiteral expr) throws PlanningException {
+    return super.visitTimestampLiteral(ctx, stack, expr);
+  }
+
+  @Override
+  public RESULT visitTimeLiteral(CONTEXT ctx, Stack<Expr> stack, TimeLiteral expr) throws PlanningException {
+    return super.visitTimeLiteral(ctx, stack, expr);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Target.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Target.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Target.java
index ac50c46..d83a394 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Target.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/Target.java
@@ -22,6 +22,7 @@ import com.google.gson.annotations.Expose;
 import org.apache.tajo.catalog.Column;
 import org.apache.tajo.common.TajoDataTypes.DataType;
 import org.apache.tajo.engine.eval.EvalNode;
+import org.apache.tajo.engine.eval.FieldEval;
 import org.apache.tajo.engine.json.CoreGsonHelper;
 import org.apache.tajo.json.GsonObject;
 import org.apache.tajo.util.TUtil;
@@ -34,14 +35,23 @@ public class Target implements Cloneable, GsonObject {
   @Expose private Column column;
   @Expose private String alias = null;
 
-  public Target(EvalNode expr) {
-    this.expr = expr;
-    this.column = new Column(expr.getName(), expr.getValueType());
+  public Target(FieldEval fieldEval) {
+    this.expr = fieldEval;
+    this.column = fieldEval.getColumnRef();
   }
 
   public Target(final EvalNode eval, final String alias) {
-    this(eval);
-    setAlias(alias);
+    this.expr = eval;
+    // force lower case
+    String normalized = alias.toLowerCase();
+
+    // If an expr is a column reference and its alias is equivalent to column name, ignore a given alias.
+    if (eval instanceof FieldEval && eval.getName().equals(normalized)) {
+      column = ((FieldEval) eval).getColumnRef();
+    } else {
+      column = new Column(normalized, eval.getValueType());
+      setAlias(alias);
+    }
   }
 
   public String getCanonicalName() {
@@ -73,7 +83,7 @@ public class Target implements Cloneable, GsonObject {
     return (T) this.expr;
   }
 
-  public Column getColumnSchema() {
+  public Column getNamedColumn() {
     return this.column;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/TargetListManager.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/TargetListManager.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/TargetListManager.java
deleted file mode 100644
index 4e1427e..0000000
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/TargetListManager.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/**
- * 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.tajo.engine.planner;
-
-import com.google.common.collect.Maps;
-import org.apache.tajo.algebra.Projection;
-import org.apache.tajo.catalog.Column;
-import org.apache.tajo.catalog.Schema;
-import org.apache.tajo.engine.eval.EvalNode;
-import org.apache.tajo.engine.eval.EvalTreeUtil;
-import org.apache.tajo.engine.eval.EvalType;
-import org.apache.tajo.engine.eval.FieldEval;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * It manages a list of targets.
- */
-public class TargetListManager {
-  private final LogicalPlan plan;
-  private boolean [] resolvedFlags;
-  private Projection projection;
-  private Target[] targets;
-  private Target[] unresolvedTargets;
-  private Map<Column, Integer> targetColumnToId = Maps.newHashMap();
-
-  public TargetListManager(LogicalPlan plan, Projection projection) {
-    this.plan = plan;
-    int targetNum = projection.size();
-    if (projection.size() == 0) {
-      resolvedFlags = new boolean[0];
-    } else {
-      resolvedFlags = new boolean[targetNum];
-    }
-    this.targets = new Target[targetNum];
-    this.unresolvedTargets = new Target[targetNum];
-  }
-
-  public TargetListManager(LogicalPlan plan, Target[] unresolvedTargets) {
-    this.plan = plan;
-
-    this.targets = new Target[unresolvedTargets.length];
-    this.unresolvedTargets = new Target[unresolvedTargets.length];
-    for (int i = 0; i < unresolvedTargets.length; i++) {
-      try {
-        this.targets[i] = (Target) unresolvedTargets[i].clone();
-        this.unresolvedTargets[i] = (Target) unresolvedTargets[i].clone();
-      } catch (CloneNotSupportedException e) {
-        e.printStackTrace();
-      }
-    }
-    resolvedFlags = new boolean[unresolvedTargets.length];
-  }
-
-  public TargetListManager(LogicalPlan plan, String blockName) {
-    this(plan, plan.getBlock(blockName).getTargetListManager().getUnresolvedTargets());
-  }
-
-  public Target getTarget(int id) {
-    return targets[id];
-  }
-
-  public Target[] getTargets() {
-    return this.targets;
-  }
-
-  public Target[] getUnresolvedTargets() {
-    return this.unresolvedTargets;
-  }
-
-  public void fill(int id, Target target) {
-    this.targets[id] = target;
-    this.unresolvedTargets[id] = target;
-
-    EvalNode evalNode = target.getEvalTree();
-    if (evalNode.getType() == EvalType.FIELD) {
-      if (target.hasAlias()) {
-        FieldEval fieldEval = (FieldEval) evalNode;
-        targetColumnToId.put(fieldEval.getColumnRef(), id);
-      }
-    }
-    targetColumnToId.put(target.getColumnSchema(), id);
-  }
-
-  public int size() {
-    return targets.length;
-  }
-
-  public void resolve(int id) {
-    resolvedFlags[id] = true;
-  }
-
-  public void resolveAll() {
-    for (int i = 0; i < resolvedFlags.length; i++) {
-      resolvedFlags[i] = true;
-    }
-  }
-
-  public boolean isResolved(int id) {
-    return resolvedFlags[id];
-  }
-
-  /**
-   * It checks whether only column reference is resolve or not.
-   * Note that this method doesn't know if an expression is resolved.
-   *
-   * @param targetColumn A column to be checked
-   * @return True if a column reference belong to a target list and is already resolved. Otherwise, false.
-   */
-  public boolean isResolve(Column targetColumn) throws PlanningException {
-    Integer targetId = targetColumnToId.get(targetColumn);
-    if (targetId == null) {
-      return false;
-    }
-    return resolvedFlags[targetId];
-  }
-
-  public Column getResolvedColumn(Column targetColumn) throws PlanningException {
-    Integer targetId = targetColumnToId.get(targetColumn);
-    if (targetId == null) {
-      throw new PlanningException("Unknown target column: " + targetColumn);
-    }
-    return getResolvedTargetToColumn(targetId);
-  }
-
-  public Target [] getUpdatedTarget(Set<Integer> exclude) throws PlanningException {
-    Target [] updated = new Target[targets.length];
-
-    for (int i = 0; i < targets.length; i++) {
-      if (targets[i] == null) { // if it is not created
-        continue;
-      }
-
-      if (!exclude.contains(i) && resolvedFlags[i]) { // if this target was evaluated, it becomes a column target.
-        Column col = getResolvedTargetToColumn(i);
-        updated[i] = new Target(new FieldEval(col));
-      } else {
-        try {
-          updated[i] = (Target) targets[i].clone();
-        } catch (CloneNotSupportedException e) {
-          throw new PlanningException(e);
-        }
-      }
-    }
-    return updated;
-  }
-
-  public Target [] getUpdatedTarget() throws PlanningException {
-    Target [] updated = new Target[targets.length];
-
-    for (int i = 0; i < targets.length; i++) {
-      if (targets[i] == null) { // if it is not created
-        continue;
-      }
-
-      if (resolvedFlags[i]) { // if this target was evaluated, it becomes a column target.
-        Column col = getResolvedTargetToColumn(i);
-        updated[i] = new Target(new FieldEval(col));
-      } else {
-        try {
-          updated[i] = (Target) targets[i].clone();
-        } catch (CloneNotSupportedException e) {
-          throw new PlanningException(e);
-        }
-      }
-    }
-    targets = updated;
-    return updated;
-  }
-
-  public Schema getUpdatedSchema() {
-    Schema schema = new Schema();
-    for (int i = 0; i < resolvedFlags.length; i++) {
-      if (resolvedFlags[i]) {
-        Column col = getResolvedTargetToColumn(i);
-        if (!schema.contains(col.getQualifiedName()))
-        schema.addColumn(col);
-      } else {
-        Collection<Column> cols = getColumnRefs(i);
-        for (Column col : cols) {
-          if (!schema.contains(col.getQualifiedName())) {
-            schema.addColumn(col);
-          }
-        }
-      }
-    }
-    return schema;
-  }
-
-  public Collection<Column> getColumnRefs(int id) {
-    return EvalTreeUtil.findDistinctRefColumns(targets[id].getEvalTree());
-  }
-
-  public Column getResolvedTargetToColumn(int id) {
-    Target t = targets[id];
-    String name;
-    if (t.hasAlias() || t.getEvalTree().getType() == EvalType.FIELD) {
-      name = t.getCanonicalName();
-    } else { // if alias name is not given or target is an expression
-      t.setAlias(plan.newNonameColumnName(t.getEvalTree().getName()));
-      name = t.getCanonicalName();
-    }
-    return new Column(name, t.getEvalTree().getValueType());
-  }
-
-  public boolean isAllResolved() {
-    for (boolean resolved : resolvedFlags) {
-      if (!resolved) {
-        return false;
-      }
-    }
-
-    return true;
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
index d05ac46..58ad73c 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
@@ -50,8 +50,7 @@ public class GlobalPlanner {
   private TajoConf conf;
   private CatalogProtos.StoreType storeType;
 
-  public GlobalPlanner(final TajoConf conf, final AbstractStorageManager sm)
-      throws IOException {
+  public GlobalPlanner(final TajoConf conf, final AbstractStorageManager sm) throws IOException {
     this.conf = conf;
     this.storeType = CatalogProtos.StoreType.valueOf(conf.getVar(TajoConf.ConfVars.SHUFFLE_FILE_FORMAT).toUpperCase());
     Preconditions.checkArgument(storeType != null);
@@ -249,8 +248,7 @@ public class GlobalPlanner {
   }
 
   private ExecutionBlock buildGroupBy(GlobalPlanContext context, ExecutionBlock childBlock,
-                                      GroupbyNode groupbyNode)
-      throws PlanningException {
+                                      GroupbyNode groupbyNode) throws PlanningException {
 
     MasterPlan masterPlan = context.plan;
     ExecutionBlock currentBlock;
@@ -259,7 +257,6 @@ public class GlobalPlanner {
       return buildDistinctGroupBy(context, childBlock, groupbyNode);
     } else {
       GroupbyNode firstPhaseGroupBy = PlannerUtil.transformGroupbyTo2P(groupbyNode);
-      firstPhaseGroupBy.setHavingCondition(null);
 
       if (firstPhaseGroupBy.getChild().getType() == NodeType.TABLE_SUBQUERY &&
           ((TableSubQueryNode)firstPhaseGroupBy.getChild()).getSubQuery().getType() == NodeType.UNION) {
@@ -365,10 +362,20 @@ public class GlobalPlanner {
 
     // if result table is not a partitioned table, directly store it
     if(partitionDesc == null) {
-      currentNode.setChild(childBlock.getPlan());
-      currentNode.setInSchema(childBlock.getPlan().getOutSchema());
-      childBlock.setPlan(currentNode);
-      return childBlock;
+
+      if (childBlock.getPlan() == null) { // when the below is union
+        for (ExecutionBlock grandChildBlock : context.plan.getChilds(childBlock)) {
+          StoreTableNode copy = PlannerUtil.clone(context.plan.getLogicalPlan(), currentNode);
+          copy.setChild(grandChildBlock.getPlan());
+          grandChildBlock.setPlan(copy);
+        }
+        return childBlock;
+      } else {
+        currentNode.setChild(childBlock.getPlan());
+        currentNode.setInSchema(childBlock.getPlan().getOutSchema());
+        childBlock.setPlan(currentNode);
+        return childBlock;
+      }
     }
 
     // if result table is a partitioned table
@@ -419,9 +426,23 @@ public class GlobalPlanner {
 
       ExecutionBlock execBlock = context.execBlockMap.remove(child.getPID());
 
-      node.setChild(execBlock.getPlan());
-      node.setInSchema(execBlock.getPlan().getOutSchema());
-      execBlock.setPlan(node);
+      if (child.getType() == NodeType.TABLE_SUBQUERY &&
+          ((TableSubQueryNode)child).getSubQuery().getType() == NodeType.UNION) {
+        MasterPlan masterPlan = context.plan;
+        for (DataChannel dataChannel : masterPlan.getIncomingChannels(execBlock.getId())) {
+          ExecutionBlock subBlock = masterPlan.getExecBlock(dataChannel.getSrcId());
+
+          ProjectionNode copy = PlannerUtil.clone(plan, node);
+          copy.setChild(subBlock.getPlan());
+          subBlock.setPlan(copy);
+        }
+        execBlock.setPlan(null);
+      } else {
+        node.setChild(execBlock.getPlan());
+        node.setInSchema(execBlock.getPlan().getOutSchema());
+        execBlock.setPlan(node);
+      }
+
       context.execBlockMap.put(node.getPID(), execBlock);
       return node;
     }
@@ -481,6 +502,20 @@ public class GlobalPlanner {
     }
 
     @Override
+    public LogicalNode visitHaving(GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+                                    HavingNode node, Stack<LogicalNode> stack) throws PlanningException {
+      LogicalNode child = super.visitHaving(context, plan, block, node, stack);
+
+      // Don't separate execution block. Having is pushed to the second grouping execution block.
+      ExecutionBlock childBlock = context.execBlockMap.remove(child.getPID());
+      node.setChild(childBlock.getPlan());
+      childBlock.setPlan(node);
+      context.execBlockMap.put(node.getPID(), childBlock);
+
+      return node;
+    }
+
+    @Override
     public LogicalNode visitGroupBy(GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
                                     GroupbyNode node, Stack<LogicalNode> stack) throws PlanningException {
       LogicalNode child = super.visitGroupBy(context, plan, block, node, stack);
@@ -553,9 +588,9 @@ public class GlobalPlanner {
       }
 
       for (ExecutionBlock childBlocks : unionBlocks) {
-        UnionNode union = (UnionNode) childBlocks.getPlan();
-        queryBlockBlocks.add(context.execBlockMap.get(union.getLeftChild().getPID()));
-        queryBlockBlocks.add(context.execBlockMap.get(union.getRightChild().getPID()));
+        for (ExecutionBlock grandChildBlock : context.plan.getChilds(childBlocks)) {
+          queryBlockBlocks.add(grandChildBlock);
+        }
       }
 
       for (ExecutionBlock childBlocks : queryBlockBlocks) {
@@ -596,7 +631,20 @@ public class GlobalPlanner {
                                           LogicalPlan.QueryBlock queryBlock,
                                           TableSubQueryNode node, Stack<LogicalNode> stack) throws PlanningException {
       LogicalNode child = super.visitTableSubQuery(context, plan, queryBlock, node, stack);
-      return handleUnaryNode(context, child, node);
+
+      ExecutionBlock currentBlock = context.execBlockMap.remove(child.getPID());
+
+      if (child.getType() == NodeType.UNION) {
+        for (ExecutionBlock childBlock : context.plan.getChilds(currentBlock.getId())) {
+          TableSubQueryNode copy = PlannerUtil.clone(plan, node);
+          copy.setSubQuery(childBlock.getPlan());
+          childBlock.setPlan(copy);
+        }
+      } else {
+        currentBlock.setPlan(node);
+      }
+      context.execBlockMap.put(node.getPID(), currentBlock);
+      return node;
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
index d0c8373..3dc5655 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
@@ -38,15 +38,21 @@ public class CreateTableNode extends LogicalNode implements Cloneable {
   @Expose private boolean external;
   @Expose private PartitionDesc partitionDesc;
 
-  public CreateTableNode(int pid, String tableName, Schema schema) {
+  public CreateTableNode(int pid) {
     super(pid, NodeType.CREATE_TABLE);
-    this.tableName = tableName;
-    this.schema = schema;
+  }
+
+  public void setTableName(String name) {
+    this.tableName = name;
   }
 
   public final String getTableName() {
     return this.tableName;
   }
+
+  public void setSchema(Schema schema) {
+    this.schema = schema;
+  }
     
   public Schema getSchema() {
     return this.schema;

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
index 6510925..4dda7af 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
@@ -24,8 +24,11 @@ public class DropTableNode extends LogicalNode {
   private String tableName;
   private boolean purge;
 
-  public DropTableNode(int pid, String tableName, boolean purge) {
+  public DropTableNode(int pid) {
     super(pid, NodeType.DROP_TABLE);
+  }
+
+  public void set(String tableName, boolean purge) {
     this.tableName = tableName;
     this.purge = purge;
   }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
index 5bc41bf..ed63bbc 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
@@ -29,9 +29,8 @@ import org.apache.tajo.util.TUtil;
 public class EvalExprNode extends LogicalNode implements Projectable {
   @Expose private Target[] exprs;
 
-  public EvalExprNode(int pid, Target[] exprs) {
+  public EvalExprNode(int pid) {
     super(pid, NodeType.EXPRS);
-    this.exprs = exprs;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
index 352780a..41f1e88 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
@@ -20,33 +20,28 @@ package org.apache.tajo.engine.planner.logical;
 
 import com.google.gson.annotations.Expose;
 import org.apache.tajo.catalog.Column;
-import org.apache.tajo.catalog.Schema;
-import org.apache.tajo.engine.eval.EvalNode;
 import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
 import org.apache.tajo.engine.planner.Target;
 import org.apache.tajo.util.TUtil;
 
 public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
 	@Expose private Column [] columns;
-  @Expose private Schema havingSchema;
-	@Expose private EvalNode havingCondition = null;
 	@Expose private Target [] targets;
   @Expose private boolean hasDistinct = false;
-	
-	public GroupbyNode(int pid, final Column [] columns) {
-		super(pid, NodeType.GROUP_BY);
-		this.columns = columns;
-	}
-	
-	public GroupbyNode(int pid, final Column [] columns, final EvalNode havingCondition) {
-    this(pid, columns);
-    this.havingCondition = havingCondition;
+
+  public GroupbyNode(int pid) {
+    super(pid, NodeType.GROUP_BY);
   }
 
   public final boolean isEmptyGrouping() {
     return columns == null || columns.length == 0;
   }
 
+  public void setGroupingColumns(Column [] groupingColumns) {
+    this.columns = groupingColumns;
+  }
+
 	public final Column [] getGroupingColumns() {
 	  return this.columns;
 	}
@@ -58,26 +53,6 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
   public void setDistinct(boolean distinct) {
     hasDistinct = distinct;
   }
-	
-	public final boolean hasHavingCondition() {
-	  return this.havingCondition != null;
-	}
-	
-	public final EvalNode getHavingCondition() {
-	  return this.havingCondition;
-	}
-	
-	public final void setHavingCondition(final EvalNode evalTree) {
-	  this.havingCondition = evalTree;
-	}
-
-  public final void setHavingSchema(Schema schema) {
-    this.havingSchema = schema;
-  }
-
-  public Schema getHavingSchema() {
-    return this.havingSchema;
-  }
 
   @Override
   public boolean hasTargets() {
@@ -87,6 +62,7 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
   @Override
   public void setTargets(Target[] targets) {
     this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
   }
 
   @Override
@@ -105,10 +81,7 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
       if(i < columns.length - 1)
         sb.append(",");
     }
-    
-    if(hasHavingCondition()) {
-      sb.append("], \"having qual\": \"").append(havingCondition).append("\"");
-    }
+
     if(hasTargets()) {
       sb.append(", \"target\": [");
       for (int i = 0; i < targets.length; i++) {
@@ -132,7 +105,6 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
       GroupbyNode other = (GroupbyNode) obj;
       boolean eq = super.equals(other);
       eq = eq && TUtil.checkEquals(columns, other.columns);
-      eq = eq && TUtil.checkEquals(havingCondition, other.havingCondition);
       eq = eq && TUtil.checkEquals(targets, other.targets);
       return eq;
     } else {
@@ -149,8 +121,7 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
         grp.columns[i] = (Column) columns[i].clone();
       }
     }
-    grp.havingCondition = (EvalNode) (havingCondition != null 
-        ? havingCondition.clone() : null);    
+
     if (targets != null) {
       grp.targets = new Target[targets.length];
       for (int i = 0; i < targets.length; i++) {
@@ -183,7 +154,7 @@ public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
     for (int i = 0; i < targets.length; i++) {
       sb.append(targets[i]);
       if( i < targets.length - 1) {
-        sb.append(",");
+        sb.append(", ");
       }
     }
     planStr.addExplan(sb.toString());

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java
new file mode 100644
index 0000000..f4eafd0
--- /dev/null
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java
@@ -0,0 +1,73 @@
+/**
+ * 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.tajo.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.eval.EvalNode;
+import org.apache.tajo.engine.planner.PlanString;
+
+public class HavingNode extends UnaryNode implements Cloneable {
+	@Expose private EvalNode qual;
+
+  public HavingNode(int pid) {
+    super(pid, NodeType.HAVING);
+  }
+
+	public EvalNode getQual() {
+		return this.qual;
+	}
+
+	public void setQual(EvalNode qual) {
+		this.qual = qual;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof HavingNode) {
+      HavingNode other = (HavingNode) obj;
+      return super.equals(other) 
+          && this.qual.equals(other.qual);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    HavingNode selNode = (HavingNode) super.clone();
+    selNode.qual = (EvalNode) this.qual.clone();
+    
+    return selNode;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString("Having: ").appendTitle(qual.toString());
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("\"Having\": {\"qual\": \"").append(qual.toString()).append("\",");
+    sb.append("\n  \"out schema\": ").append(getOutSchema()).append(",");
+    sb.append("\n  \"in schema\": ").append(getInSchema()).append("}");
+
+    return sb.toString()+"\n"
+        + getChild().toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
index 7272630..e82cc3e 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
@@ -35,7 +35,6 @@ public class IndexScanNode extends ScanNode {
     super(pid, scanNode.getTableDesc());
     setQual(scanNode.getQual());
     setInSchema(scanNode.getInSchema());
-    setOutSchema(scanNode.getOutSchema());
     setTargets(scanNode.getTargets());
     setType(NodeType.BST_INDEX_SCAN);
     this.sortKeys = sortKeys;

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
index 23a79df..2628c3b 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
@@ -25,6 +25,7 @@ import com.google.gson.annotations.Expose;
 import org.apache.tajo.algebra.JoinType;
 import org.apache.tajo.engine.eval.EvalNode;
 import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
 import org.apache.tajo.engine.planner.Target;
 import org.apache.tajo.util.TUtil;
 
@@ -87,6 +88,7 @@ public class JoinNode extends BinaryNode implements Projectable, Cloneable {
   @Override
   public void setTargets(Target[] targets) {
     this.targets = targets;
+    this.setOutSchema(PlannerUtil.targetToSchema(targets));
   }
 
   @Override
@@ -97,8 +99,21 @@ public class JoinNode extends BinaryNode implements Projectable, Cloneable {
       planStr.addExplan("Join Cond: " + joinQual.toString());
     }
 
-    planStr.addDetail("in schema: " + getInSchema());
+    if (hasTargets()) {
+      planStr.addExplan("target list: ");
+      boolean first = true;
+      for (Target target : targets) {
+        if (!first) {
+          planStr.appendExplain(", ");
+        }
+        planStr.appendExplain(target.toString());
+        first = false;
+      }
+    }
+
     planStr.addDetail("out schema: " + getOutSchema());
+    planStr.addDetail("in schema: " + getInSchema());
+
     return planStr;
   }
 
@@ -108,14 +123,8 @@ public class JoinNode extends BinaryNode implements Projectable, Cloneable {
       JoinNode other = (JoinNode) obj;
       boolean eq = this.joinType.equals(other.joinType);
       eq &= TUtil.checkEquals(this.targets, other.targets);
-      if (this.joinQual != null && other.joinQual != null) {
-        eq &= this.joinQual.equals(other.joinQual);
-      } else if (this.joinQual == null && other.joinQual == null) {
-
-      } else {
-        eq = false;
-      }
-      return eq;
+      eq &= TUtil.checkEquals(joinQual, other.joinQual);
+      return eq && leftChild.equals(other.leftChild) && rightChild.equals(other.rightChild);
     } else {
       return false;
     }
@@ -126,6 +135,12 @@ public class JoinNode extends BinaryNode implements Projectable, Cloneable {
     JoinNode join = (JoinNode) super.clone();
     join.joinType = this.joinType;
     join.joinQual = this.joinQual == null ? null : (EvalNode) this.joinQual.clone();
+    if (hasTargets()) {
+      join.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        join.targets[i] = (Target) targets[i].clone();
+      }
+    }
     return join;
   }
 

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
index b604fac..197a5bc 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
@@ -24,9 +24,12 @@ import org.apache.tajo.engine.planner.PlanString;
 public final class LimitNode extends UnaryNode implements Cloneable {
 	@Expose private long fetchFirstNum;
 
-  public LimitNode(int pid, long fetchFirstNum) {
+  public LimitNode(int pid) {
     super(pid, NodeType.LIMIT);
-    this.fetchFirstNum = fetchFirstNum;
+  }
+
+  public void setFetchFirst(long num) {
+    this.fetchFirstNum = num;
   }
   
   public long getFetchFirstNum() {

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
index 4c0dd11..32b9bc6 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
@@ -32,6 +32,7 @@ public enum NodeType {
   PROJECTION(ProjectionNode.class),
   LIMIT(LimitNode.class),
   SORT(SortNode.class),
+  HAVING(HavingNode.class),
   GROUP_BY(GroupbyNode.class),
   SELECTION(SelectionNode.class),
   JOIN(JoinNode.class),

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
index c2807fe..d72b31e 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
@@ -18,10 +18,17 @@
 
 package org.apache.tajo.engine.planner.logical;
 
+import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.engine.planner.Target;
 
 public interface Projectable {
   boolean hasTargets();
+
   void setTargets(Target[] targets);
+
   Target [] getTargets();
+
+  public Schema getInSchema();
+
+  public Schema getOutSchema();
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
index 8ef2bda..1a219a6 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
@@ -20,6 +20,7 @@ package org.apache.tajo.engine.planner.logical;
 
 import com.google.gson.annotations.Expose;
 import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
 import org.apache.tajo.engine.planner.Target;
 import org.apache.tajo.util.TUtil;
 
@@ -32,12 +33,8 @@ public class ProjectionNode extends UnaryNode implements Projectable {
   @Expose	private Target [] targets;
   @Expose private boolean distinct = false;
 
-  /**
-   * @param targets they should be all evaluated ones.
-   */
-	public ProjectionNode(int pid, Target [] targets) {
+	public ProjectionNode(int pid) {
 		super(pid, NodeType.PROJECTION);
-		this.targets = targets;
 	}
 
   public boolean hasTargets() {
@@ -47,6 +44,7 @@ public class ProjectionNode extends UnaryNode implements Projectable {
   @Override
   public void setTargets(Target[] targets) {
     this.targets = targets;
+    this.setOutSchema(PlannerUtil.targetToSchema(targets));
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
index 5ea61b4..8ed247e 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
@@ -20,6 +20,16 @@ package org.apache.tajo.engine.planner.logical;
 
 import org.apache.tajo.catalog.Schema;
 
+/**
+ * It provides a logical view of a relation. Regarding a table, the main difference between a logical view and a
+ * physical view is as follows:
+ *
+ * <ul>
+ * <li>In logical view, each column in the table has qualified name by table alias name. In addition, the schema of
+ * logical view will includes partition columns if we use column-partitioned tables.</li>
+ * <li>In contrast, in physical view: each column in the table has qualified name by the original table.</li>
+ * </ul>
+ */
 public abstract class RelationNode extends LogicalNode {
 
   public RelationNode(int pid, NodeType nodeType) {

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
index cd9c1f1..165e1e8 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
@@ -90,6 +90,7 @@ public class ScanNode extends RelationNode implements Projectable {
   @Override
 	public void setTargets(Target [] targets) {
 	  this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
 	}
 
   @Override

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SelectionNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SelectionNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SelectionNode.java
index 699a561..b39ade3 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SelectionNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SelectionNode.java
@@ -24,11 +24,10 @@ import org.apache.tajo.engine.planner.PlanString;
 
 public class SelectionNode extends UnaryNode implements Cloneable {
 	@Expose private EvalNode qual;
-	
-	public SelectionNode(int pid, EvalNode qual) {
-		super(pid, NodeType.SELECTION);
-		setQual(qual);
-	}
+
+  public SelectionNode(int pid) {
+    super(pid, NodeType.SELECTION);
+  }
 
 	public EvalNode getQual() {
 		return this.qual;

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SortNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SortNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SortNode.java
index 46ceea8..00c8485 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SortNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/SortNode.java
@@ -20,7 +20,6 @@ package org.apache.tajo.engine.planner.logical;
 
 import com.google.common.base.Preconditions;
 import com.google.gson.annotations.Expose;
-import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.catalog.SortSpec;
 import org.apache.tajo.engine.planner.PlanString;
 import org.apache.tajo.util.TUtil;
@@ -28,17 +27,13 @@ import org.apache.tajo.util.TUtil;
 public final class SortNode extends UnaryNode implements Cloneable {
 	@Expose private SortSpec [] sortKeys;
 
-  public SortNode(int pid, SortSpec[] sortKeys) {
+  public SortNode(int pid) {
     super(pid, NodeType.SORT);
-    Preconditions.checkArgument(sortKeys.length > 0, 
-        "At least one sort key must be specified");
-    this.sortKeys = sortKeys;
   }
 
-  public SortNode(int pid, SortSpec[] sortKeys, Schema inSchema, Schema outSchema) {
-    this(pid, sortKeys);
-    this.setInSchema(inSchema);
-    this.setOutSchema(outSchema);
+  public void setSortSpecs(SortSpec[] sortSpecs) {
+    Preconditions.checkArgument(sortSpecs.length > 0, "At least one sort key must be specified");
+    this.sortKeys = sortSpecs;
   }
   
   public SortSpec[] getSortKeys() {

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/TableSubQueryNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/TableSubQueryNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/TableSubQueryNode.java
index 335d12f..25e5d07 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/TableSubQueryNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/TableSubQueryNode.java
@@ -24,6 +24,7 @@ import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.engine.planner.PlanString;
 import org.apache.tajo.engine.planner.PlannerUtil;
 import org.apache.tajo.engine.planner.Target;
+import org.apache.tajo.util.TUtil;
 
 public class TableSubQueryNode extends RelationNode implements Projectable {
   @Expose private String tableName;
@@ -33,10 +34,13 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
   public TableSubQueryNode(int pid, String tableName, LogicalNode subQuery) {
     super(pid, NodeType.TABLE_SUBQUERY);
     this.tableName = PlannerUtil.normalizeTableName(tableName);
-    this.subQuery = subQuery;
-    setOutSchema((Schema) this.subQuery.getOutSchema().clone());
-    getOutSchema().setQualifier(this.tableName);
-    setInSchema((Schema) this.subQuery.getInSchema().clone());
+    if (subQuery != null) {
+      this.subQuery = subQuery;
+      setOutSchema((Schema) this.subQuery.getOutSchema().clone());
+      setInSchema((Schema) this.subQuery.getOutSchema().clone());
+      getInSchema().setQualifier(this.tableName);
+      getOutSchema().setQualifier(this.tableName);
+    }
   }
 
   public String getTableName() {
@@ -55,7 +59,10 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
 
   public void setSubQuery(LogicalNode node) {
     this.subQuery = node;
-    setInSchema(subQuery.getInSchema());
+    setInSchema((Schema) this.subQuery.getOutSchema().clone());
+    getInSchema().setQualifier(this.tableName);
+    setOutSchema((Schema) this.subQuery.getOutSchema().clone());
+    getOutSchema().setQualifier(this.tableName);
   }
 
   public LogicalNode getSubQuery() {
@@ -70,6 +77,7 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
   @Override
   public void setTargets(Target[] targets) {
     this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
   }
 
   @Override
@@ -81,6 +89,24 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
   public PlanString getPlanString() {
     PlanString planStr = new PlanString("TablePrimarySubQuery");
     planStr.appendTitle(" as ").appendTitle(tableName);
+
+    if (hasTargets()) {
+      StringBuilder sb = new StringBuilder("Targets: ");
+      for (int i = 0; i < targets.length; i++) {
+        sb.append(targets[i]);
+        if( i < targets.length - 1) {
+          sb.append(", ");
+        }
+      }
+      planStr.addExplan(sb.toString());
+      if (getOutSchema() != null) {
+        planStr.addExplan("out schema: " + getOutSchema().toString());
+      }
+      if (getInSchema() != null) {
+        planStr.addExplan("in  schema: " + getInSchema().toString());
+      }
+    }
+
     return planStr;
   }
 
@@ -103,6 +129,13 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
   public Object clone() throws CloneNotSupportedException {
     TableSubQueryNode newTableSubQueryNode = (TableSubQueryNode) super.clone();
     newTableSubQueryNode.tableName = tableName;
+    newTableSubQueryNode.subQuery = (LogicalNode) subQuery.clone();
+    if (hasTargets()) {
+      newTableSubQueryNode.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        newTableSubQueryNode.targets[i] = (Target) targets[i].clone();
+      }
+    }
     return newTableSubQueryNode;
   }
 
@@ -119,6 +152,14 @@ public class TableSubQueryNode extends RelationNode implements Projectable {
   }
 
   public String toString() {
-    return "(" + getPID() + ") Table Subquery (alias = " + tableName + ")\n" + subQuery.toString();
+    StringBuilder sb = new StringBuilder();
+    sb.append("(").append(getPID()).append(") Table Subquery (alias=").append(tableName).append(")\n");
+    if (hasTargets()) {
+      sb.append("  targets: ").append(TUtil.arrayToString(targets)).append("\n");
+    }
+    sb.append("  out schema:").append(getOutSchema()).append("\n");
+    sb.append("  input schema:").append(getInSchema()).append("\n");
+    sb.append(subQuery.toString());
+    return sb.toString();
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-tajo/blob/8e1f989a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/UnionNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/UnionNode.java b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/UnionNode.java
index d0e8b02..56ca146 100644
--- a/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/UnionNode.java
+++ b/tajo-core/tajo-core-backend/src/main/java/org/apache/tajo/engine/planner/logical/UnionNode.java
@@ -25,19 +25,13 @@ import org.apache.tajo.engine.planner.PlanString;
 
 public class UnionNode extends BinaryNode {
 
-  public UnionNode(int pid, LogicalNode outer, LogicalNode inner) {
+  public UnionNode(int pid) {
     super(pid, NodeType.UNION);
-    setLeftChild(outer);
-    setRightChild(inner);
   }
 
-
   @Override
   public PlanString getPlanString() {
     PlanString planStr = new PlanString("Union");
-    planStr.appendTitle(" (L - " + ((TableSubQueryNode)getLeftChild()).getTableName());
-    planStr.appendTitle(", R - " + ((TableSubQueryNode)getRightChild()).getTableName());
-    planStr.appendTitle(")");
     return planStr;
   }