You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by ji...@apache.org on 2014/10/26 06:46:05 UTC

[06/33] TAJO-1125: Separate logical plan and optimizer into a maven module.

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
new file mode 100644
index 0000000..a916d5c
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
@@ -0,0 +1,304 @@
+/**
+ * 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.plan.joinorder;
+
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.PlanningException;
+import org.apache.tajo.plan.util.SchemaUtil;
+import org.apache.tajo.plan.expr.AlgebraicUtil;
+import org.apache.tajo.plan.logical.*;
+import org.apache.tajo.util.TUtil;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+/**
+ * This is a greedy heuristic algorithm to find a bushy join tree. This algorithm finds
+ * the best join order with join conditions and pushed-down join conditions to
+ * all join operators.
+ */
+public class GreedyHeuristicJoinOrderAlgorithm implements JoinOrderAlgorithm {
+  public static double DEFAULT_SELECTION_FACTOR = 0.1;
+
+  @Override
+  public FoundJoinOrder findBestOrder(LogicalPlan plan, LogicalPlan.QueryBlock block, JoinGraph joinGraph,
+                                      Set<String> relationsWithoutQual) throws PlanningException {
+
+    // Setup a remain relation set to be joined
+    // Why we should use LinkedHashSet? - it should keep the deterministic for the order of joins.
+    // Otherwise, join orders can be different even if join costs are the same to each other.
+    Set<LogicalNode> remainRelations = new LinkedHashSet<LogicalNode>();
+    for (RelationNode relation : block.getRelations()) {
+      remainRelations.add(relation);
+    }
+
+    LogicalNode latestJoin;
+    JoinEdge bestPair;
+
+    while (remainRelations.size() > 1) {
+      // Find the best join pair among all joinable operators in candidate set.
+      bestPair = getBestPair(plan, joinGraph, remainRelations);
+
+      remainRelations.remove(bestPair.getLeftRelation()); // remainRels = remainRels \ Ti
+      remainRelations.remove(bestPair.getRightRelation()); // remainRels = remainRels \ Tj
+
+      latestJoin = createJoinNode(plan, bestPair);
+      remainRelations.add(latestJoin);
+
+      // all logical nodes should be registered to corresponding blocks
+      block.registerNode(latestJoin);
+    }
+
+    JoinNode joinTree = (JoinNode) remainRelations.iterator().next();
+    // all generated nodes should be registered to corresponding blocks
+    block.registerNode(joinTree);
+    return new FoundJoinOrder(joinTree, getCost(joinTree));
+  }
+
+  private static JoinNode createJoinNode(LogicalPlan plan, JoinEdge joinEdge) {
+    LogicalNode left = joinEdge.getLeftRelation();
+    LogicalNode right = joinEdge.getRightRelation();
+
+    JoinNode joinNode = plan.createNode(JoinNode.class);
+
+    if (PlannerUtil.isCommutativeJoin(joinEdge.getJoinType())) {
+      // if only one operator is relation
+      if ((left instanceof RelationNode) && !(right instanceof RelationNode)) {
+        // for left deep
+        joinNode.init(joinEdge.getJoinType(), right, left);
+      } else {
+        // if both operators are relation or if both are relations
+        // we don't need to concern the left-right position.
+        joinNode.init(joinEdge.getJoinType(), left, right);
+      }
+    } else {
+      joinNode.init(joinEdge.getJoinType(), left, right);
+    }
+
+    Schema mergedSchema = SchemaUtil.merge(joinNode.getLeftChild().getOutSchema(),
+        joinNode.getRightChild().getOutSchema());
+    joinNode.setInSchema(mergedSchema);
+    joinNode.setOutSchema(mergedSchema);
+    if (joinEdge.hasJoinQual()) {
+      joinNode.setJoinQual(AlgebraicUtil.createSingletonExprFromCNF(joinEdge.getJoinQual()));
+    }
+    return joinNode;
+  }
+
+  /**
+   * Find the best join pair among all joinable operators in candidate set.
+   *
+   * @param plan a logical plan
+   * @param graph a join graph which consists of vertices and edges, where vertex is relation and
+   *              each edge is join condition.
+   * @param candidateSet candidate operators to be joined.
+   * @return The best join pair among them
+   * @throws PlanningException
+   */
+  private JoinEdge getBestPair(LogicalPlan plan, JoinGraph graph, Set<LogicalNode> candidateSet)
+      throws PlanningException {
+    double minCost = Double.MAX_VALUE;
+    JoinEdge bestJoin = null;
+
+    double minNonCrossJoinCost = Double.MAX_VALUE;
+    JoinEdge bestNonCrossJoin = null;
+
+    for (LogicalNode outer : candidateSet) {
+      for (LogicalNode inner : candidateSet) {
+        if (outer.equals(inner)) {
+          continue;
+        }
+
+        JoinEdge foundJoin = findJoin(plan, graph, outer, inner);
+        if (foundJoin == null) {
+          continue;
+        }
+        double cost = getCost(foundJoin);
+
+        if (cost < minCost) {
+          minCost = cost;
+          bestJoin = foundJoin;
+        }
+
+        // Keep the min cost join
+        // But, if there exists a qualified join, the qualified join must be chosen
+        // rather than cross join regardless of cost.
+        if (foundJoin.hasJoinQual()) {
+          if (cost < minNonCrossJoinCost) {
+            minNonCrossJoinCost = cost;
+            bestNonCrossJoin = foundJoin;
+          }
+        }
+      }
+    }
+
+    if (bestNonCrossJoin != null) {
+      return bestNonCrossJoin;
+    } else {
+      return bestJoin;
+    }
+  }
+
+  /**
+   * Find a join between two logical operator trees
+   *
+   * @return If there is no join condition between two relation, it returns NULL value.
+   */
+  private static JoinEdge findJoin(LogicalPlan plan, JoinGraph graph, LogicalNode outer, LogicalNode inner)
+      throws PlanningException {
+    JoinEdge foundJoinEdge = null;
+
+    // If outer is outer join, make edge key using all relation names in outer.
+    SortedSet<String> relationNames =
+        new TreeSet<String>(PlannerUtil.getRelationLineageWithinQueryBlock(plan, outer));
+    String outerEdgeKey = TUtil.collectionToString(relationNames, ", ");
+    for (String innerName : PlannerUtil.getRelationLineageWithinQueryBlock(plan, inner)) {
+      if (graph.hasEdge(outerEdgeKey, innerName)) {
+        JoinEdge existJoinEdge = graph.getEdge(outerEdgeKey, innerName);
+        if (foundJoinEdge == null) {
+          foundJoinEdge = new JoinEdge(existJoinEdge.getJoinType(), outer, inner,
+              existJoinEdge.getJoinQual());
+        } else {
+          foundJoinEdge.addJoinQual(AlgebraicUtil.createSingletonExprFromCNF(
+              existJoinEdge.getJoinQual()));
+        }
+      }
+    }
+    if (foundJoinEdge != null) {
+      return foundJoinEdge;
+    }
+
+    relationNames =
+        new TreeSet<String>(PlannerUtil.getRelationLineageWithinQueryBlock(plan, inner));
+    outerEdgeKey = TUtil.collectionToString(relationNames, ", ");
+    for (String outerName : PlannerUtil.getRelationLineageWithinQueryBlock(plan, outer)) {
+      if (graph.hasEdge(outerEdgeKey, outerName)) {
+        JoinEdge existJoinEdge = graph.getEdge(outerEdgeKey, outerName);
+        if (foundJoinEdge == null) {
+          foundJoinEdge = new JoinEdge(existJoinEdge.getJoinType(), inner, outer,
+              existJoinEdge.getJoinQual());
+        } else {
+          foundJoinEdge.addJoinQual(AlgebraicUtil.createSingletonExprFromCNF(
+              existJoinEdge.getJoinQual()));
+        }
+      }
+    }
+    if (foundJoinEdge != null) {
+      return foundJoinEdge;
+    }
+
+    for (String outerName : PlannerUtil.getRelationLineageWithinQueryBlock(plan, outer)) {
+      for (String innerName : PlannerUtil.getRelationLineageWithinQueryBlock(plan, inner)) {
+
+        // Find all joins between two relations and merge them into one join if possible
+        if (graph.hasEdge(outerName, innerName)) {
+          JoinEdge existJoinEdge = graph.getEdge(outerName, innerName);
+          if (foundJoinEdge == null) {
+            foundJoinEdge = new JoinEdge(existJoinEdge.getJoinType(), outer, inner,
+                existJoinEdge.getJoinQual());
+          } else {
+            foundJoinEdge.addJoinQual(AlgebraicUtil.createSingletonExprFromCNF(
+                existJoinEdge.getJoinQual()));
+          }
+        }
+      }
+    }
+
+    if (foundJoinEdge == null) {
+      foundJoinEdge = new JoinEdge(JoinType.CROSS, outer, inner);
+    }
+
+    return foundJoinEdge;
+  }
+
+  /**
+   * Getting a cost of one join
+   * @param joinEdge
+   * @return
+   */
+  public static double getCost(JoinEdge joinEdge) {
+    double filterFactor = 1;
+    if (joinEdge.hasJoinQual()) {
+      // TODO - should consider join type
+      // TODO - should statistic information obtained from query history
+      filterFactor = filterFactor * Math.pow(DEFAULT_SELECTION_FACTOR, joinEdge.getJoinQual().length);
+      return getCost(joinEdge.getLeftRelation()) * getCost(joinEdge.getRightRelation()) * filterFactor;
+    } else {
+      // make cost bigger if cross join
+      return Math.pow(getCost(joinEdge.getLeftRelation()) * getCost(joinEdge.getRightRelation()), 2);
+    }
+  }
+
+  // TODO - costs of other operator operators (e.g., group-by and sort) should be computed in proper manners.
+  public static double getCost(LogicalNode node) {
+    switch (node.getType()) {
+
+    case PROJECTION:
+      ProjectionNode projectionNode = (ProjectionNode) node;
+      return getCost(projectionNode.getChild());
+
+    case JOIN:
+      JoinNode joinNode = (JoinNode) node;
+      double filterFactor = 1;
+      if (joinNode.hasJoinQual()) {
+        filterFactor = Math.pow(DEFAULT_SELECTION_FACTOR,
+            AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual()).length);
+        return getCost(joinNode.getLeftChild()) * getCost(joinNode.getRightChild()) * filterFactor;
+      } else {
+        return Math.pow(getCost(joinNode.getLeftChild()) * getCost(joinNode.getRightChild()), 2);
+      }
+
+    case SELECTION:
+      SelectionNode selectionNode = (SelectionNode) node;
+      return getCost(selectionNode.getChild()) *
+          Math.pow(DEFAULT_SELECTION_FACTOR, AlgebraicUtil.toConjunctiveNormalFormArray(selectionNode.getQual()).length);
+
+    case TABLE_SUBQUERY:
+      TableSubQueryNode subQueryNode = (TableSubQueryNode) node;
+      return getCost(subQueryNode.getSubQuery());
+
+    case SCAN:
+      ScanNode scanNode = (ScanNode) node;
+      if (scanNode.getTableDesc().getStats() != null) {
+        double cost = ((ScanNode)node).getTableDesc().getStats().getNumBytes();
+        return cost;
+      } else {
+        return Long.MAX_VALUE;
+      }
+
+    case UNION:
+      UnionNode unionNode = (UnionNode) node;
+      return getCost(unionNode.getLeftChild()) + getCost(unionNode.getRightChild());
+
+    case EXCEPT:
+    case INTERSECT:
+      throw new UnsupportedOperationException("getCost() does not support EXCEPT or INTERSECT yet");
+
+    default:
+      // all binary operators (join, union, except, and intersect) are handled in the above cases.
+      // So, we need to handle only unary nodes in default.
+      return getCost(((UnaryNode) node).getChild());
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinEdge.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinEdge.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinEdge.java
new file mode 100644
index 0000000..ce06748
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinEdge.java
@@ -0,0 +1,75 @@
+/**
+ * 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.plan.joinorder;
+
+import com.google.common.collect.Sets;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.plan.logical.LogicalNode;
+import org.apache.tajo.util.TUtil;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class JoinEdge {
+  private final JoinType joinType;
+  private final LogicalNode leftRelation;
+  private final LogicalNode rightRelation;
+  private final Set<EvalNode> joinQual = Sets.newHashSet();
+
+  public JoinEdge(JoinType joinType, LogicalNode leftRelation, LogicalNode rightRelation) {
+    this.joinType = joinType;
+    this.leftRelation = leftRelation;
+    this.rightRelation = rightRelation;
+  }
+
+  public JoinEdge(JoinType joinType, LogicalNode leftRelation, LogicalNode rightRelation,
+                  EvalNode ... condition) {
+    this(joinType, leftRelation, rightRelation);
+    Collections.addAll(joinQual, condition);
+  }
+
+  public JoinType getJoinType() {
+    return joinType;
+  }
+
+  public LogicalNode getLeftRelation() {
+    return leftRelation;
+  }
+
+  public LogicalNode getRightRelation() {
+    return rightRelation;
+  }
+
+  public boolean hasJoinQual() {
+    return joinQual.size() > 0;
+  }
+
+  public void addJoinQual(EvalNode joinQual) {
+    this.joinQual.add(joinQual);
+  }
+
+  public EvalNode [] getJoinQual() {
+    return joinQual.toArray(new EvalNode[joinQual.size()]);
+  }
+
+  public String toString() {
+    return leftRelation + " " + joinType + " " + rightRelation + " ON " + TUtil.collectionToString(joinQual, ", ");
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
new file mode 100644
index 0000000..f7fc30e
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
@@ -0,0 +1,154 @@
+/**
+ * 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.plan.joinorder;
+
+import com.google.common.collect.Sets;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.util.graph.SimpleUndirectedGraph;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.NamedExprsManager;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.PlanningException;
+import org.apache.tajo.plan.expr.AlgebraicUtil;
+import org.apache.tajo.plan.expr.BinaryEval;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.plan.expr.EvalTreeUtil;
+import org.apache.tajo.plan.logical.JoinNode;
+import org.apache.tajo.plan.logical.RelationNode;
+import org.apache.tajo.util.TUtil;
+
+import java.util.*;
+
+public class JoinGraph extends SimpleUndirectedGraph<String, JoinEdge> {
+
+  private String [] guessRelationsFromJoinQual(LogicalPlan.QueryBlock block, BinaryEval joinCondition)
+      throws PlanningException {
+
+    // Note that we can guarantee that each join qual used here is a singleton.
+    // This is because we use dissect a join qual into conjunctive normal forms.
+    // In other words, each join qual has a form 'col1 = col2'.
+    Column leftExpr = EvalTreeUtil.findAllColumnRefs(joinCondition.getLeftExpr()).get(0);
+    Column rightExpr = EvalTreeUtil.findAllColumnRefs(joinCondition.getRightExpr()).get(0);
+
+    // 0 - left table, 1 - right table
+    String [] relationNames = new String[2];
+
+    NamedExprsManager namedExprsMgr = block.getNamedExprsManager();
+    if (leftExpr.hasQualifier()) {
+      relationNames[0] = leftExpr.getQualifier();
+    } else {
+      if (namedExprsMgr.isAliasedName(leftExpr.getSimpleName())) {
+        String columnName = namedExprsMgr.getOriginalName(leftExpr.getSimpleName());
+        String qualifier = CatalogUtil.extractQualifier(columnName);
+        relationNames[0] = qualifier;
+      } else {
+        // search for a relation which evaluates a right term included in a join condition
+        for (RelationNode rel : block.getRelations()) {
+          if (rel.getOutSchema().contains(leftExpr)) {
+            String qualifier = rel.getCanonicalName();
+            relationNames[0] = qualifier;
+          }
+        }
+
+        if (relationNames[0] == null) { // if not found
+          throw new PlanningException("Cannot expect a referenced relation: " + leftExpr);
+        }
+      }
+    }
+
+    if (rightExpr.hasQualifier()) {
+      relationNames[1] = rightExpr.getQualifier();
+    } else {
+      if (namedExprsMgr.isAliasedName(rightExpr.getSimpleName())) {
+        String columnName = namedExprsMgr.getOriginalName(rightExpr.getSimpleName());
+        String qualifier = CatalogUtil.extractQualifier(columnName);
+        relationNames[1] = qualifier;
+      } else {
+        // search for a relation which evaluates a right term included in a join condition
+        for (RelationNode rel : block.getRelations()) {
+          if (rel.getOutSchema().contains(rightExpr)) {
+            String qualifier = rel.getCanonicalName();
+            relationNames[1] = qualifier;
+          }
+        }
+
+        if (relationNames[1] == null) { // if not found
+          throw new PlanningException("Cannot expect a referenced relation: " + rightExpr);
+        }
+      }
+    }
+
+    return relationNames;
+  }
+
+  public Collection<EvalNode> addJoin(LogicalPlan plan, LogicalPlan.QueryBlock block,
+                                      JoinNode joinNode) throws PlanningException {
+    if (joinNode.getJoinType() == JoinType.LEFT_OUTER || joinNode.getJoinType() == JoinType.RIGHT_OUTER) {
+      JoinEdge edge = new JoinEdge(joinNode.getJoinType(),
+            joinNode.getLeftChild(), joinNode.getRightChild(), joinNode.getJoinQual());
+
+      SortedSet<String> leftNodeRelationName =
+          new TreeSet<String>(PlannerUtil.getRelationLineageWithinQueryBlock(plan, joinNode.getLeftChild()));
+      SortedSet<String> rightNodeRelationName =
+          new TreeSet<String>(PlannerUtil.getRelationLineageWithinQueryBlock(plan, joinNode.getRightChild()));
+
+      addEdge(
+          TUtil.collectionToString(leftNodeRelationName, ", "),
+          TUtil.collectionToString(rightNodeRelationName, ", "),
+          edge);
+
+      Set<EvalNode> allInOneCnf = new HashSet<EvalNode>();
+      allInOneCnf.add(joinNode.getJoinQual());
+
+      return allInOneCnf;
+    } else {
+      Set<EvalNode> cnf = Sets.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual()));
+
+      for (EvalNode singleQual : cnf) {
+        if (EvalTreeUtil.isJoinQual(block, singleQual, true)) {
+          String[] relations = guessRelationsFromJoinQual(block, (BinaryEval) singleQual);
+          String leftExprRelName = relations[0];
+          String rightExprRelName = relations[1];
+
+          Collection<String> leftLineage = PlannerUtil.getRelationLineageWithinQueryBlock(plan, joinNode.getLeftChild());
+
+          boolean isLeftExprForLeftTable = leftLineage.contains(leftExprRelName);
+
+          JoinEdge edge = getEdge(leftExprRelName, rightExprRelName);
+          if (edge != null) {
+            edge.addJoinQual(singleQual);
+          } else {
+            if (isLeftExprForLeftTable) {
+              edge = new JoinEdge(joinNode.getJoinType(),
+                  block.getRelation(leftExprRelName), block.getRelation(rightExprRelName), singleQual);
+              addEdge(leftExprRelName, rightExprRelName, edge);
+            } else {
+              edge = new JoinEdge(joinNode.getJoinType(),
+                  block.getRelation(rightExprRelName), block.getRelation(leftExprRelName), singleQual);
+              addEdge(rightExprRelName, leftExprRelName, edge);
+            }
+          }
+        }
+      }
+      return cnf;
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderAlgorithm.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderAlgorithm.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderAlgorithm.java
new file mode 100644
index 0000000..2dd0670
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderAlgorithm.java
@@ -0,0 +1,48 @@
+/**
+ * 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.plan.joinorder;
+
+import org.apache.hadoop.classification.InterfaceStability;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.PlanningException;
+import org.apache.tajo.plan.joinorder.FoundJoinOrder;
+import org.apache.tajo.plan.joinorder.JoinGraph;
+
+import java.util.Set;
+
+/**
+ * An interface for join order algorithms
+ */
+@InterfaceStability.Evolving
+public interface JoinOrderAlgorithm {
+
+  /**
+   *
+   * @param plan
+   * @param block
+   * @param joinGraph A join graph represents join conditions and their connections among relations.
+   *                  Given a graph, each vertex represents a relation, and each edge contains a join condition.
+   *                  A join graph does not contain relations that do not have any corresponding join condition.
+   * @param relationsWithoutQual The names of relations that do not have any corresponding join condition.
+   * @return
+   * @throws org.apache.tajo.plan.PlanningException
+   */
+  FoundJoinOrder findBestOrder(LogicalPlan plan, LogicalPlan.QueryBlock block, JoinGraph joinGraph,
+                               Set<String> relationsWithoutQual) throws PlanningException;
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTableNode.java
new file mode 100644
index 0000000..e9e2467
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTableNode.java
@@ -0,0 +1,134 @@
+/**
+ * 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.plan.logical;
+
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.algebra.AlterTableOpType;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.plan.PlanString;
+
+public class AlterTableNode extends LogicalNode {
+
+  @Expose
+  private String tableName;
+  @Expose
+  private String newTableName;
+  @Expose
+  private String columnName;
+  @Expose
+  private String newColumnName;
+  @Expose
+  private Column addNewColumn;
+  @Expose
+  private AlterTableOpType alterTableOpType;
+
+  public AlterTableNode(int pid) {
+    super(pid, NodeType.ALTER_TABLE);
+  }
+
+  public String getTableName() {
+    return tableName;
+  }
+
+  public void setTableName(String tableName) {
+    this.tableName = tableName;
+  }
+
+  public String getNewTableName() {
+    return newTableName;
+  }
+
+  public void setNewTableName(String newTableName) {
+    this.newTableName = newTableName;
+  }
+
+  public String getColumnName() {
+    return columnName;
+  }
+
+  public void setColumnName(String columnName) {
+    this.columnName = columnName;
+  }
+
+  public String getNewColumnName() {
+    return newColumnName;
+  }
+
+  public void setNewColumnName(String newColumnName) {
+    this.newColumnName = newColumnName;
+  }
+
+  public Column getAddNewColumn() {
+    return addNewColumn;
+  }
+
+  public void setAddNewColumn(Column addNewColumn) {
+    this.addNewColumn = addNewColumn;
+  }
+
+  public AlterTableOpType getAlterTableOpType() {
+    return alterTableOpType;
+  }
+
+  public void setAlterTableOpType(AlterTableOpType alterTableOpType) {
+    this.alterTableOpType = alterTableOpType;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof AlterTableNode) {
+      AlterTableNode other = (AlterTableNode) obj;
+      return super.equals(other);
+    } else {
+      return false;
+    }
+  }
+
+    /*@Override
+    public Object clone() throws CloneNotSupportedException {
+        AlterTableNode alterTableNode = (AlterTableNode) super.clone();
+        alterTableNode.tableName = tableName;
+        alterTableNode.newTableName = newTableName;
+        alterTableNode.columnName = columnName;
+        alterTableNode.newColumnName=newColumnName;
+        alterTableNode.addNewColumn =(Column) addNewColumn.clone();
+        return alterTableNode;
+    }*/
+
+  @Override
+  public String toString() {
+    return "AlterTable (table=" + tableName + ")";
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTablespaceNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTablespaceNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTablespaceNode.java
new file mode 100644
index 0000000..7b79cc1
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/AlterTablespaceNode.java
@@ -0,0 +1,104 @@
+/**
+ * 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.plan.logical;
+
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.algebra.AlterTablespaceSetType;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.logical.LogicalNode;
+import org.apache.tajo.plan.logical.LogicalNodeVisitor;
+import org.apache.tajo.plan.logical.NodeType;
+
+public class AlterTablespaceNode extends LogicalNode implements Cloneable {
+
+  @Expose private String tablespaceName;
+  @Expose private AlterTablespaceSetType setType;
+  @Expose private String uri;
+
+
+  public AlterTablespaceNode(int pid) {
+    super(pid, NodeType.ALTER_TABLESPACE);
+  }
+
+  public String getTablespaceName() {
+    return tablespaceName;
+  }
+
+  public void setTablespaceName(String tablespaceName) {
+    this.tablespaceName = tablespaceName;
+  }
+
+  public AlterTablespaceSetType getSetType() {
+    return setType;
+  }
+
+  public String getLocation() {
+    return uri;
+  }
+
+  public void setLocation(String uri) {
+    this.setType = AlterTablespaceSetType.LOCATION;
+    this.uri = uri;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof AlterTablespaceNode) {
+      AlterTablespaceNode other = (AlterTablespaceNode) obj;
+      return super.equals(other);
+    } else {
+      return false;
+    }
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(tablespaceName, setType, uri);
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    AlterTablespaceNode alter = (AlterTablespaceNode) super.clone();
+    alter.tablespaceName = tablespaceName;
+    alter.setType = setType;
+    alter.uri = uri;
+    return alter;
+  }
+
+  @Override
+  public String toString() {
+    return "AlterTablespace (space=" + tablespaceName + ")";
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/BinaryNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/BinaryNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/BinaryNode.java
new file mode 100644
index 0000000..709ef34
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/BinaryNode.java
@@ -0,0 +1,77 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.json.GsonObject;
+
+public abstract class BinaryNode extends LogicalNode implements Cloneable, GsonObject {
+	@Expose LogicalNode leftChild = null;
+	@Expose LogicalNode rightChild = null;
+
+	public BinaryNode(int pid, NodeType nodeType) {
+		super(pid, nodeType);
+	}
+	
+	public <T extends LogicalNode> T getLeftChild() {
+		return (T) this.leftChild;
+	}
+	
+	public void setLeftChild(LogicalNode op) {
+		this.leftChild = op;
+	}
+
+	public <T extends LogicalNode> T getRightChild() {
+		return (T) this.rightChild;
+	}
+
+	public void setRightChild(LogicalNode op) {
+		this.rightChild = op;
+	}
+
+  public boolean deepEquals(Object o) {
+    if (o instanceof BinaryNode) {
+      BinaryNode b = (BinaryNode) o;
+      return equals(o) &&
+          leftChild.deepEquals(b.leftChild) && rightChild.deepEquals(b.rightChild);
+    }
+    return false;
+  }
+	
+	@Override
+  public Object clone() throws CloneNotSupportedException {
+	  BinaryNode binNode = (BinaryNode) super.clone();
+	  binNode.leftChild = (LogicalNode) leftChild.clone();
+	  binNode.rightChild = (LogicalNode) rightChild.clone();
+	  
+	  return binNode;
+	}
+	
+	public void preOrder(LogicalNodeVisitor visitor) {
+	  visitor.visit(this);
+	  leftChild.postOrder(visitor);
+    rightChild.postOrder(visitor);
+  }
+	
+	public void postOrder(LogicalNodeVisitor visitor) {
+    leftChild.postOrder(visitor);
+    rightChild.postOrder(visitor);
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateDatabaseNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateDatabaseNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateDatabaseNode.java
new file mode 100644
index 0000000..e3f73fe
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateDatabaseNode.java
@@ -0,0 +1,87 @@
+/*
+ * 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.plan.logical;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.plan.PlanString;
+
+public class CreateDatabaseNode extends LogicalNode implements Cloneable {
+  private String databaseName;
+  private boolean ifNotExists;
+
+  public CreateDatabaseNode(int pid) {
+    super(pid, NodeType.CREATE_DATABASE);
+  }
+
+  public void init(String databaseName, boolean ifNotExists) {
+    this.databaseName = databaseName;
+    this.ifNotExists = ifNotExists;
+  }
+
+  public String getDatabaseName() {
+    return this.databaseName;
+  }
+
+  public boolean isIfNotExists() {
+    return ifNotExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifNotExists ? " IF NOT EXISTS " : " ").appendTitle(databaseName);
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(databaseName, ifNotExists);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof CreateDatabaseNode) {
+      CreateDatabaseNode other = (CreateDatabaseNode) obj;
+      return super.equals(other) && this.databaseName.equals(other.databaseName) && ifNotExists == other.ifNotExists;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateDatabaseNode newNode = (CreateDatabaseNode) super.clone();
+    newNode.databaseName = databaseName;
+    newNode.ifNotExists = ifNotExists;
+    return newNode;
+  }
+
+  @Override
+  public String toString() {
+    return "CREATE DATABASE " + (ifNotExists ? " IF NOT EXISTS " : "")
+        + CatalogUtil.denormalizeIdentifier(databaseName);
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateTableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateTableNode.java
new file mode 100644
index 0000000..d03da6a
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/CreateTableNode.java
@@ -0,0 +1,145 @@
+/**
+ * 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.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.KeyValueSet;
+import org.apache.tajo.util.TUtil;
+
+public class CreateTableNode extends StoreTableNode implements Cloneable {
+  @Expose private Schema schema;
+  @Expose private Path path;
+  @Expose private boolean external;
+  @Expose private boolean ifNotExists;
+
+  public CreateTableNode(int pid) {
+    super(pid, NodeType.CREATE_TABLE);
+  }
+
+  public void setTableSchema(Schema schema) {
+    this.schema = schema;
+  }
+    
+  public Schema getTableSchema() {
+    return this.schema;
+  }
+
+  public Schema getLogicalSchema() {
+    if (hasPartition()) {
+      Schema logicalSchema = new Schema(schema);
+      logicalSchema.addColumns(getPartitionMethod().getExpressionSchema());
+      return logicalSchema;
+    } else {
+      return schema;
+    }
+  }
+
+  public boolean hasPath() {
+    return this.path != null;
+  }
+
+  public void setPath(Path path) {
+    this.path = path;
+  }
+  
+  public Path getPath() {
+    return this.path;
+  }
+
+  public boolean isExternal() {
+    return external;
+  }
+
+  public void setExternal(boolean external) {
+    this.external = external;
+  }
+
+  public boolean hasSubQuery() {
+    return child != null;
+  }
+
+  public void setIfNotExists(boolean ifNotExists) {
+    this.ifNotExists = ifNotExists;
+  }
+
+  public boolean isIfNotExists() {
+    return ifNotExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+
+  public int hashCode() {
+    return super.hashCode() ^ Objects.hashCode(schema, path, external, ifNotExists) * 31;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof CreateTableNode) {
+      CreateTableNode other = (CreateTableNode) obj;
+      return super.equals(other)
+          && this.schema.equals(other.schema)
+          && this.external == other.external
+          && TUtil.checkEquals(path, other.path)
+          && ifNotExists == other.ifNotExists;
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateTableNode createTableNode = (CreateTableNode) super.clone();
+    createTableNode.tableName = tableName;
+    createTableNode.schema = (Schema) schema.clone();
+    createTableNode.storageType = storageType;
+    createTableNode.external = external;
+    createTableNode.path = path != null ? new Path(path.toString()) : null;
+    createTableNode.options = (KeyValueSet) (options != null ? options.clone() : null);
+    createTableNode.ifNotExists = ifNotExists;
+    return createTableNode;
+  }
+
+  public String toString() {
+    return "CreateTable (table=" + tableName + ", external=" + external + ", storeType=" + storageType +
+        ", ifNotExists=" + ifNotExists +")";
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    if (hasSubQuery()) {
+      child.preOrder(visitor);
+    }
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+    if (hasSubQuery()) {
+      child.preOrder(visitor);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DistinctGroupbyNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DistinctGroupbyNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DistinctGroupbyNode.java
new file mode 100644
index 0000000..e31e488
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DistinctGroupbyNode.java
@@ -0,0 +1,251 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.AggregationFunctionCallEval;
+import org.apache.tajo.util.TUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class DistinctGroupbyNode extends UnaryNode implements Projectable, Cloneable {
+  @Expose
+  private GroupbyNode groupbyPlan;
+
+  @Expose
+  private List<GroupbyNode> groupByNodes;
+
+  @Expose
+  private Target[] targets;
+
+  @Expose
+  private Column[] groupingColumns;
+
+  @Expose
+  private int[] resultColumnIds;
+
+  /** Aggregation Functions */
+  @Expose private AggregationFunctionCallEval[] aggrFunctions;
+
+  public DistinctGroupbyNode(int pid) {
+    super(pid, NodeType.DISTINCT_GROUP_BY);
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return targets != null && targets.length > 0;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    if (hasTargets()) {
+      return targets;
+    } else {
+      return new Target[0];
+    }
+  }
+
+  public void setGroupbyNodes(List<GroupbyNode> groupByNodes) {
+    this.groupByNodes =  groupByNodes;
+  }
+
+  public List<GroupbyNode> getGroupByNodes() {
+    return groupByNodes;
+  }
+
+  public final Column[] getGroupingColumns() {
+    return groupingColumns;
+  }
+
+  public final void setGroupColumns(Column[] groupingColumns) {
+    this.groupingColumns = groupingColumns;
+  }
+
+  public int[] getResultColumnIds() {
+    return resultColumnIds;
+  }
+
+  public void setResultColumnIds(int[] resultColumnIds) {
+    this.resultColumnIds = resultColumnIds;
+  }
+
+  public AggregationFunctionCallEval [] getAggFunctions() {
+    return this.aggrFunctions;
+  }
+
+  public void setAggFunctions(AggregationFunctionCallEval[] evals) {
+    this.aggrFunctions = evals;
+  }
+
+  public void setGroupbyPlan(GroupbyNode groupbyPlan) { this.groupbyPlan = groupbyPlan; }
+
+  public GroupbyNode getGroupbyPlan() { return this.groupbyPlan; }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DistinctGroupbyNode cloneNode = (DistinctGroupbyNode)super.clone();
+
+    if (groupingColumns != null) {
+      cloneNode.groupingColumns = new Column[groupingColumns.length];
+      for (int i = 0; i < groupingColumns.length; i++) {
+        cloneNode.groupingColumns[i] = groupingColumns[i];
+      }
+    }
+
+    if (groupByNodes != null) {
+      cloneNode.groupByNodes = new ArrayList<GroupbyNode>();
+      for (GroupbyNode eachNode: groupByNodes) {
+        GroupbyNode groupbyNode = (GroupbyNode)eachNode.clone();
+        groupbyNode.setPID(-1);
+        cloneNode.groupByNodes.add(groupbyNode);
+      }
+    }
+
+    if (targets != null) {
+      cloneNode.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        cloneNode.targets[i] = (Target) targets[i].clone();
+      }
+    }
+
+    if (groupbyPlan != null) {
+      cloneNode.groupbyPlan = (GroupbyNode)groupbyPlan.clone();
+    }
+    return cloneNode;
+  }
+
+  public final boolean isEmptyGrouping() {
+    return groupingColumns == null || groupingColumns.length == 0;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Distinct GroupBy (");
+    if (groupingColumns != null || groupingColumns.length > 0) {
+      sb.append("grouping set=").append(TUtil.arrayToString(groupingColumns));
+      sb.append(", ");
+    }
+    for (GroupbyNode eachNode: groupByNodes) {
+      sb.append(", groupbyNode=").append(eachNode.toString());
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof DistinctGroupbyNode) {
+      DistinctGroupbyNode other = (DistinctGroupbyNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && TUtil.checkEquals(groupingColumns, other.groupingColumns);
+      eq = eq && TUtil.checkEquals(groupByNodes, other.groupByNodes);
+      eq = eq && TUtil.checkEquals(targets, other.targets);
+      return eq;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("(");
+    Column [] groupingColumns = this.groupingColumns;
+    for (int j = 0; j < groupingColumns.length; j++) {
+      sb.append(groupingColumns[j].getSimpleName());
+      if(j < groupingColumns.length - 1) {
+        sb.append(",");
+      }
+    }
+
+    sb.append(")");
+
+    planStr.appendTitle(sb.toString());
+
+    sb = new StringBuilder();
+    sb.append("(");
+
+    String prefix = "";
+    for (GroupbyNode eachNode: groupByNodes) {
+      if (eachNode.hasAggFunctions()) {
+        AggregationFunctionCallEval[] aggrFunctions = eachNode.getAggFunctions();
+        for (int j = 0; j < aggrFunctions.length; j++) {
+          sb.append(prefix).append(aggrFunctions[j]);
+          prefix = ",";
+        }
+      }
+    }
+    sb.append(")");
+    planStr.appendExplain("exprs: ").appendExplain(sb.toString());
+
+    sb = new StringBuilder("target list: ");
+    for (int i = 0; i < targets.length; i++) {
+      sb.append(targets[i]);
+      if( i < targets.length - 1) {
+        sb.append(", ");
+      }
+    }
+    planStr.addExplan(sb.toString());
+
+    planStr.addDetail("out schema:").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema:").appendDetail(getInSchema().toString());
+
+    for (GroupbyNode eachNode: groupByNodes) {
+      planStr.addDetail("\t").appendDetail("distinct: " + eachNode.isDistinct())
+          .appendDetail(", " + eachNode.getShortPlanString());
+    }
+
+    return planStr;
+  }
+
+  public Column[] getFirstStageShuffleKeyColumns() {
+    List<Column> shuffleKeyColumns = new ArrayList<Column>();
+    shuffleKeyColumns.add(getOutSchema().getColumn(0));   //distinctseq column
+    if (groupingColumns != null) {
+      for (Column eachColumn: groupingColumns) {
+        if (!shuffleKeyColumns.contains(eachColumn)) {
+          shuffleKeyColumns.add(eachColumn);
+        }
+      }
+    }
+    for (GroupbyNode eachGroupbyNode: groupByNodes) {
+      if (eachGroupbyNode.getGroupingColumns() != null && eachGroupbyNode.getGroupingColumns().length > 0) {
+        for (Column eachColumn: eachGroupbyNode.getGroupingColumns()) {
+          if (!shuffleKeyColumns.contains(eachColumn)) {
+            shuffleKeyColumns.add(eachColumn);
+          }
+        }
+      }
+    }
+
+    return shuffleKeyColumns.toArray(new Column[]{});
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropDatabaseNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropDatabaseNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropDatabaseNode.java
new file mode 100644
index 0000000..b88c384
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropDatabaseNode.java
@@ -0,0 +1,85 @@
+/*
+ * 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.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+
+public class DropDatabaseNode extends LogicalNode implements Cloneable {
+  @Expose private String databaseName;
+  @Expose private boolean ifExists;
+
+  public DropDatabaseNode(int pid) {
+    super(pid, NodeType.DROP_DATABASE);
+  }
+
+  public void init(String databaseName, boolean ifExists) {
+    this.databaseName = databaseName;
+    this.ifExists = ifExists;
+  }
+
+  public String getDatabaseName() {
+    return this.databaseName;
+  }
+
+  public boolean isIfExists() {
+    return ifExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifExists ? " IF EXISTS " : " ").appendTitle(databaseName);
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(databaseName, ifExists);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DropDatabaseNode) {
+      DropDatabaseNode other = (DropDatabaseNode) obj;
+      return super.equals(other) && this.databaseName.equals(other.databaseName) && ifExists == other.ifExists;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DropDatabaseNode dropTableNode = (DropDatabaseNode) super.clone();
+    dropTableNode.databaseName = databaseName;
+    return dropTableNode;
+  }
+
+  @Override
+  public String toString() {
+    return "DROP DATABASE " + (ifExists ? "IF EXISTS ":"") + databaseName;
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropTableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropTableNode.java
new file mode 100644
index 0000000..1a61852
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/DropTableNode.java
@@ -0,0 +1,95 @@
+/*
+ * 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.plan.logical;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.plan.PlanString;
+
+public class DropTableNode extends LogicalNode implements Cloneable {
+  private String tableName;
+  private boolean ifExists;
+  private boolean purge;
+
+  public DropTableNode(int pid) {
+    super(pid, NodeType.DROP_TABLE);
+  }
+
+  public void init(String tableName, boolean ifExists, boolean purge) {
+    this.tableName = tableName;
+    this.ifExists = ifExists;
+    this.purge = purge;
+  }
+
+  public String getTableName() {
+    return this.tableName;
+  }
+
+  public boolean isIfExists() {
+    return this.ifExists;
+  }
+
+  public boolean isPurge() {
+    return this.purge;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifExists ? " IF EXISTS" : "").appendTitle(purge ? " PURGE" : "");
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(tableName, ifExists, purge);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DropTableNode) {
+      DropTableNode other = (DropTableNode) obj;
+      return super.equals(other) &&
+          this.tableName.equals(other.tableName) &&
+          this.ifExists == other.ifExists &&
+          this.purge == other.purge;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DropTableNode dropTableNode = (DropTableNode) super.clone();
+    dropTableNode.tableName = tableName;
+    dropTableNode.ifExists = ifExists;
+    dropTableNode.purge = purge;
+    return dropTableNode;
+  }
+
+  @Override
+  public String toString() {
+    return "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + tableName + (purge ? " PURGE" : "");
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/EvalExprNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/EvalExprNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/EvalExprNode.java
new file mode 100644
index 0000000..2519165
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/EvalExprNode.java
@@ -0,0 +1,85 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.util.TUtil;
+
+public class EvalExprNode extends LogicalNode implements Projectable {
+  @Expose private Target[] exprs;
+
+  public EvalExprNode(int pid) {
+    super(pid, NodeType.EXPRS);
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return true;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.exprs = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return exprs;
+  }
+
+  public Target[] getExprs() {
+    return this.exprs;
+  }
+  
+  @Override
+  public String toString() {
+    return "EvalExprNode (" + TUtil.arrayToString(exprs) + ")";
+  }
+
+  public boolean equals(Object object) {
+    if (object instanceof EvalExprNode) {
+      EvalExprNode other = (EvalExprNode) object;
+      return TUtil.checkEquals(this.exprs, other.exprs);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    // nothing
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    // nothing
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return null;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ExceptNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ExceptNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ExceptNode.java
new file mode 100644
index 0000000..221b62c
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ExceptNode.java
@@ -0,0 +1,45 @@
+/**
+ * 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.plan.logical;
+
+import org.apache.tajo.plan.PlanString;
+
+public class ExceptNode extends BinaryNode {
+
+  public ExceptNode(int pid) {
+    super(pid, NodeType.EXCEPT);
+  }
+
+  public void init(LogicalNode left, LogicalNode right) {
+    setLeftChild(left);
+    setRightChild(right);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.appendTitle(" (L - " + ((TableSubQueryNode)getLeftChild()).getTableName());
+    planStr.appendTitle(", R - " + ((TableSubQueryNode)getRightChild()).getTableName());
+    planStr.appendTitle(")");
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupElement.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupElement.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupElement.java
new file mode 100644
index 0000000..a969c30
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupElement.java
@@ -0,0 +1,64 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.algebra.Aggregation;
+import org.apache.tajo.plan.expr.EvalNode;
+
+public class GroupElement implements Cloneable {
+  @Expose private Aggregation.GroupType type;
+  @Expose private EvalNode [] groupingSets;
+
+  @SuppressWarnings("unused")
+  public GroupElement() {
+    // for gson
+  }
+
+  public GroupElement(Aggregation.GroupType type, EvalNode [] groupingSets) {
+    this.type = type;
+    this.groupingSets = groupingSets;
+  }
+
+  public Aggregation.GroupType getType() {
+    return this.type;
+  }
+
+  public EvalNode[] getGroupingSets() {
+    return this.groupingSets;
+  }
+
+  public String toString() {
+    Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation()
+        .setPrettyPrinting().create();
+    return gson.toJson(this);
+  }
+
+  public Object clone() throws CloneNotSupportedException {
+    GroupElement groups = (GroupElement) super.clone();
+    groups.type = type;
+    groups.groupingSets = new EvalNode [groupingSets.length];
+    for (int i = 0; i < groupingSets.length; i++) {
+      groups.groupingSets[i++] = (EvalNode) groupingSets[i].clone();
+    }
+    return groups;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupbyNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupbyNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupbyNode.java
new file mode 100644
index 0000000..2c74ce3
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/GroupbyNode.java
@@ -0,0 +1,254 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.AggregationFunctionCallEval;
+import org.apache.tajo.util.TUtil;
+
+public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
+	/** Grouping key sets */
+  @Expose private Column [] groupingColumns;
+  /** Aggregation Functions */
+  @Expose private AggregationFunctionCallEval [] aggrFunctions;
+  /**
+   * It's a list of targets. The grouping columns should be followed by aggregation functions.
+   * aggrFunctions keep actual aggregation functions, but it only contains field references.
+   * */
+  @Expose private Target [] targets;
+  @Expose private boolean hasDistinct = false;
+
+  public GroupbyNode(int pid) {
+    super(pid, NodeType.GROUP_BY);
+  }
+
+  public final boolean isEmptyGrouping() {
+    return groupingColumns == null || groupingColumns.length == 0;
+  }
+
+  public void setGroupingColumns(Column [] groupingColumns) {
+    this.groupingColumns = groupingColumns;
+  }
+
+	public final Column [] getGroupingColumns() {
+	  return this.groupingColumns;
+	}
+
+  public final boolean isDistinct() {
+    return hasDistinct;
+  }
+
+  public void setDistinct(boolean distinct) {
+    hasDistinct = distinct;
+  }
+
+  public boolean hasAggFunctions() {
+    return this.aggrFunctions != null;
+  }
+
+  public AggregationFunctionCallEval[] getAggFunctions() {
+    return this.aggrFunctions;
+  }
+
+  public void setAggFunctions(AggregationFunctionCallEval[] evals) {
+    this.aggrFunctions = evals;
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return this.targets != null;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return this.targets;
+  }
+  
+  public void setChild(LogicalNode subNode) {
+    super.setChild(subNode);
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder("GroupBy (");
+    if (groupingColumns != null || groupingColumns.length > 0) {
+      sb.append("grouping set=").append(TUtil.arrayToString(groupingColumns));
+      sb.append(", ");
+    }
+    if (hasAggFunctions()) {
+      sb.append("funcs=").append(TUtil.arrayToString(aggrFunctions));
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof GroupbyNode) {
+      GroupbyNode other = (GroupbyNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && TUtil.checkEquals(groupingColumns, other.groupingColumns);
+      eq = eq && TUtil.checkEquals(aggrFunctions, other.aggrFunctions);
+      eq = eq && TUtil.checkEquals(targets, other.targets);
+      return eq;
+    } else {
+      return false;  
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    GroupbyNode grp = (GroupbyNode) super.clone();
+    if (groupingColumns != null) {
+      grp.groupingColumns = new Column[groupingColumns.length];
+      for (int i = 0; i < groupingColumns.length; i++) {
+        grp.groupingColumns[i] = groupingColumns[i];
+      }
+    }
+
+    if (aggrFunctions != null) {
+      grp.aggrFunctions = new AggregationFunctionCallEval[aggrFunctions.length];
+      for (int i = 0; i < aggrFunctions.length; i++) {
+        grp.aggrFunctions[i] = (AggregationFunctionCallEval) aggrFunctions[i].clone();
+      }
+    }
+
+    if (targets != null) {
+      grp.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        grp.targets[i] = (Target) targets[i].clone();
+      }
+    }
+
+    return grp;
+  }
+
+  public String getShortPlanString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getType().name() + "(" + getPID() + ")").append("(");
+    Column [] groupingColumns = this.groupingColumns;
+    for (int j = 0; j < groupingColumns.length; j++) {
+      sb.append(groupingColumns[j].getSimpleName());
+      if(j < groupingColumns.length - 1) {
+        sb.append(",");
+      }
+    }
+
+    sb.append(")");
+
+    // there can be no aggregation functions
+    if (hasAggFunctions()) {
+      sb.append(", exprs: (");
+
+      for (int j = 0; j < aggrFunctions.length; j++) {
+        sb.append(aggrFunctions[j]);
+        if(j < aggrFunctions.length - 1) {
+          sb.append(",");
+        }
+      }
+      sb.append(")");
+    }
+
+    if (targets != null) {
+      sb.append(", target list:{");
+      for (int i = 0; i < targets.length; i++) {
+        sb.append(targets[i]);
+        if (i < targets.length - 1) {
+          sb.append(", ");
+        }
+      }
+      sb.append("}");
+    }
+    sb.append(", out schema:").append(getOutSchema().toString());
+    sb.append(", in schema:").append(getInSchema().toString());
+
+    return sb.toString();
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("(");
+    Column [] groupingColumns = this.groupingColumns;
+    for (int j = 0; j < groupingColumns.length; j++) {
+      sb.append(groupingColumns[j].getSimpleName());
+      if(j < groupingColumns.length - 1) {
+        sb.append(",");
+      }
+    }
+
+    sb.append(")");
+
+    planStr.appendTitle(sb.toString());
+
+    // there can be no aggregation functions
+    if (hasAggFunctions()) {
+      sb = new StringBuilder();
+      sb.append("(");
+
+      for (int j = 0; j < aggrFunctions.length; j++) {
+        sb.append(aggrFunctions[j]);
+        if(j < aggrFunctions.length - 1) {
+          sb.append(",");
+        }
+      }
+      sb.append(")");
+      planStr.appendExplain("exprs: ").appendExplain(sb.toString());
+    }
+
+    sb = new StringBuilder("target list: ");
+    for (int i = 0; i < targets.length; i++) {
+      sb.append(targets[i]);
+      if( i < targets.length - 1) {
+        sb.append(", ");
+      }
+    }
+    planStr.addExplan(sb.toString());
+
+    planStr.addDetail("out schema:").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema:").appendDetail(getInSchema().toString());
+
+    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/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/HavingNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/HavingNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/HavingNode.java
new file mode 100644
index 0000000..a3371c7
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/HavingNode.java
@@ -0,0 +1,72 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.expr.EvalNode;
+
+public class HavingNode extends UnaryNode implements SelectableNode, Cloneable {
+	@Expose private EvalNode qual;
+
+  public HavingNode(int pid) {
+    super(pid, NodeType.HAVING);
+  }
+
+  @Override
+  public boolean hasQual() {
+    return true;
+  }
+
+  public void setQual(EvalNode qual) {
+		this.qual = qual;
+  }
+
+  public EvalNode getQual() {
+    return this.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(this).appendTitle(" (").appendTitle(qual.toString()).appendTitle(")");
+  }
+
+  public String toString() {
+    return "Having (filter=" + qual + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IndexScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IndexScanNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IndexScanNode.java
new file mode 100644
index 0000000..0d59733
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IndexScanNode.java
@@ -0,0 +1,122 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.plan.serder.PlanGsonHelper;
+
+public class IndexScanNode extends ScanNode {
+  @Expose private SortSpec [] sortKeys;
+  @Expose private Schema keySchema = null;
+  @Expose private Datum[] datum = null;
+  
+  public IndexScanNode(int pid, ScanNode scanNode ,
+      Schema keySchema , Datum[] datum, SortSpec[] sortKeys ) {
+    super(pid);
+    init(scanNode.getTableDesc());
+    setQual(scanNode.getQual());
+    setInSchema(scanNode.getInSchema());
+    setTargets(scanNode.getTargets());
+    setType(NodeType.BST_INDEX_SCAN);
+    this.sortKeys = sortKeys;
+    this.keySchema = keySchema;
+    this.datum = datum;
+  }
+  
+  public SortSpec[] getSortKeys() {
+    return this.sortKeys;
+  }
+  
+  public Schema getKeySchema() {
+    return this.keySchema;
+  }
+  
+  public Datum[] getDatum() {
+    return this.datum;
+  }
+  
+  public void setSortKeys(SortSpec[] sortKeys) {
+    this.sortKeys = sortKeys;
+  }
+  
+  public void setKeySchema( Schema keySchema ) {
+    this.keySchema = keySchema;
+  }
+
+  @Override
+  public String toString() {
+    Gson gson = PlanGsonHelper.getInstance();
+    StringBuilder builder = new StringBuilder();
+    builder.append("IndexScanNode : {\n");
+    builder.append("  \"keySchema\" : \"" + gson.toJson(this.keySchema) + "\"\n");
+    builder.append("  \"sortKeys\" : \"" + gson.toJson(this.sortKeys) + " \"\n");
+    builder.append("  \"datums\" : \"" + gson.toJson(this.datum) + "\"\n");
+    builder.append("      <<\"superClass\" : " + super.toString());
+    builder.append(">>}");
+    builder.append("}");
+    return builder.toString();
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof IndexScanNode) {
+      IndexScanNode other = (IndexScanNode) obj;
+      
+      boolean eq = super.equals(other);
+      eq = eq && this.sortKeys.length == other.sortKeys.length;
+      if(eq) {
+        for(int i = 0 ; i < this.sortKeys.length ; i ++) {
+          eq = eq && this.sortKeys[i].getSortKey().equals(
+              other.sortKeys[i].getSortKey());
+          eq = eq && this.sortKeys[i].isAscending()
+              == other.sortKeys[i].isAscending();
+          eq = eq && this.sortKeys[i].isNullFirst()
+              == other.sortKeys[i].isNullFirst();
+        }
+      }
+      if(eq) {
+        for(int i = 0 ; i < this.datum.length ; i ++ ) {
+          eq = eq && this.datum[i].equals(other.datum[i]);
+        }
+      }
+     return eq;
+    }   
+    return false;
+  } 
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    IndexScanNode indexNode = (IndexScanNode) super.clone();
+    indexNode.keySchema = (Schema) this.keySchema.clone();
+    indexNode.sortKeys = new SortSpec[this.sortKeys.length];
+    for(int i = 0 ; i < sortKeys.length ; i ++ )
+      indexNode.sortKeys[i] = (SortSpec) this.sortKeys[i].clone();
+    indexNode.datum = new Datum[this.datum.length];
+    for(int i = 0 ; i < datum.length ; i ++ ) {
+      indexNode.datum[i] = this.datum[i];
+    }
+    return indexNode;
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/InsertNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/InsertNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/InsertNode.java
new file mode 100644
index 0000000..f860f08
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/InsertNode.java
@@ -0,0 +1,182 @@
+/**
+ * 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.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.TUtil;
+
+public class InsertNode extends StoreTableNode implements Cloneable {
+  @Expose private boolean overwrite;
+  @Expose private Schema tableSchema;
+
+  /** a target schema of a target table */
+  @Expose private Schema targetSchema;
+  /** a output schema of select clause */
+  @Expose private Schema projectedSchema;
+  @Expose private Path path;
+
+  public InsertNode(int pid) {
+    super(pid, NodeType.INSERT);
+  }
+
+  public void setTargetTable(TableDesc desc) {
+    setTableName(desc.getName());
+    if (desc.hasPartition()) {
+      tableSchema = desc.getLogicalSchema();
+    } else {
+      tableSchema = desc.getSchema();
+    }
+    setPath(desc.getPath());
+    setOptions(desc.getMeta().getOptions());
+    setStorageType(desc.getMeta().getStoreType());
+
+    if (desc.hasPartition()) {
+      this.setPartitionMethod(desc.getPartitionMethod());
+    }
+  }
+
+  public void setTargetLocation(Path path) {
+    this.path = path;
+  }
+
+  public void setSubQuery(LogicalNode subQuery) {
+    this.setChild(subQuery);
+    this.setInSchema(subQuery.getOutSchema());
+    this.setOutSchema(subQuery.getOutSchema());
+  }
+
+  public boolean isOverwrite() {
+    return overwrite;
+  }
+
+  public void setOverwrite(boolean overwrite) {
+    this.overwrite = overwrite;
+  }
+
+  public Schema getTableSchema() {
+    return tableSchema;
+  }
+
+  public void setTableSchema(Schema tableSchema) {
+    this.tableSchema = tableSchema;
+  }
+
+  public boolean hasTargetSchema() {
+    return this.targetSchema != null;
+  }
+
+  public Schema getTargetSchema() {
+    return this.targetSchema;
+  }
+
+  public void setTargetSchema(Schema schema) {
+    this.targetSchema = schema;
+  }
+
+  public Schema getProjectedSchema() {
+    return this.projectedSchema;
+  }
+
+  public void setProjectedSchema(Schema projected) {
+    this.projectedSchema = projected;
+  }
+
+  public boolean hasPath() {
+    return this.path != null;
+  }
+
+  public void setPath(Path path) {
+    this.path = path;
+  }
+  
+  public Path getPath() {
+    return this.path;
+  }
+
+  public boolean hasStorageType() {
+    return this.storageType != null;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof InsertNode) {
+      InsertNode other = (InsertNode) obj;
+      return super.equals(other)
+          && this.overwrite == other.overwrite
+          && TUtil.checkEquals(this.tableSchema, other.tableSchema)
+          && TUtil.checkEquals(this.targetSchema, other.targetSchema)
+          && TUtil.checkEquals(path, other.path);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    InsertNode insertNode = (InsertNode) super.clone();
+    insertNode.overwrite = overwrite;
+    insertNode.tableSchema = new Schema(tableSchema);
+    insertNode.targetSchema = targetSchema != null ? new Schema(targetSchema) : null;
+    insertNode.path = path != null ? new Path(path.toString()) : null;
+    return insertNode;
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Insert (overwrite=").append(overwrite);
+    if (hasTargetTable()) {
+      sb.append(",table=").append(tableName);
+    }
+    if (hasPath()) {
+      sb.append(", location=").append(path);
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    getChild().preOrder(visitor);
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+    getChild().postOrder(visitor);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planString = new PlanString(this);
+    planString.appendTitle(" INTO ");
+    if (hasTargetTable()) {
+      planString.appendTitle(getTableName());
+      if (hasTargetSchema()) {
+        planString.addExplan(getTargetSchema().toString());
+      }
+    } else {
+      planString.addExplan("LOCATION " + path);
+    }
+    return planString;
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IntersectNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IntersectNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IntersectNode.java
new file mode 100644
index 0000000..3f1bf0d
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/IntersectNode.java
@@ -0,0 +1,44 @@
+/**
+ * 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.plan.logical;
+
+import org.apache.tajo.plan.PlanString;
+
+public class IntersectNode extends BinaryNode {
+  public IntersectNode(int pid) {
+    super(pid, NodeType.INTERSECT);
+  }
+
+  public void init(LogicalNode left, LogicalNode right) {
+    setLeftChild(left);
+    setRightChild(right);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.appendTitle(" (L - " + ((TableSubQueryNode)getLeftChild()).getTableName());
+    planStr.appendTitle(", R - " + ((TableSubQueryNode)getRightChild()).getTableName());
+    planStr.appendTitle(")");
+    return planStr;
+  }
+}