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 2015/08/14 16:30:20 UTC

[41/51] [partial] tajo git commit: TAJO-1761: Separate an integration unit test kit into an independent module.

http://git-wip-us.apache.org/repos/asf/tajo/blob/a4106883/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlanner.java b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlanner.java
new file mode 100644
index 0000000..02e921a
--- /dev/null
+++ b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlanner.java
@@ -0,0 +1,1360 @@
+/**
+ * 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.base.Preconditions;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
+import org.apache.tajo.LocalTajoTestingUtility;
+import org.apache.tajo.QueryVars;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.TajoTestingCluster;
+import org.apache.tajo.algebra.AlterTableOpType;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.benchmark.TPCH;
+import org.apache.tajo.catalog.*;
+import org.apache.tajo.catalog.partition.PartitionMethodDesc;
+import org.apache.tajo.catalog.proto.CatalogProtos;
+import org.apache.tajo.catalog.proto.CatalogProtos.FunctionType;
+import org.apache.tajo.common.TajoDataTypes.Type;
+import org.apache.tajo.datum.TextDatum;
+import org.apache.tajo.engine.function.FunctionLoader;
+import org.apache.tajo.engine.function.builtin.SumInt;
+import org.apache.tajo.engine.json.CoreGsonHelper;
+import org.apache.tajo.engine.parser.SQLAnalyzer;
+import org.apache.tajo.engine.query.QueryContext;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.LogicalOptimizer;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.LogicalPlanner;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.*;
+import org.apache.tajo.plan.logical.*;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.session.Session;
+import org.apache.tajo.storage.TablespaceManager;
+import org.apache.tajo.util.CommonTestingUtil;
+import org.apache.tajo.util.FileUtil;
+import org.apache.tajo.util.KeyValueSet;
+import org.apache.tajo.util.TUtil;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.*;
+
+import static org.apache.tajo.TajoConstants.DEFAULT_DATABASE_NAME;
+import static org.apache.tajo.TajoConstants.DEFAULT_TABLESPACE_NAME;
+import static org.junit.Assert.*;
+
+public class TestLogicalPlanner {
+  private static TajoTestingCluster util;
+  private static CatalogService catalog;
+  private static SQLAnalyzer sqlAnalyzer;
+  private static LogicalPlanner planner;
+  private static TPCH tpch;
+  private static Session session = LocalTajoTestingUtility.createDummySession();
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    util = new TajoTestingCluster();
+    util.startCatalogCluster();
+    catalog = util.getMiniCatalogCluster().getCatalog();
+    catalog.createTablespace(DEFAULT_TABLESPACE_NAME, "hdfs://localhost:1234");
+    catalog.createDatabase(DEFAULT_DATABASE_NAME, DEFAULT_TABLESPACE_NAME);
+
+    for (FunctionDesc funcDesc : FunctionLoader.findLegacyFunctions()) {
+      catalog.createFunction(funcDesc);
+    }
+
+    Schema schema = new Schema();
+    schema.addColumn("name", Type.TEXT);
+    schema.addColumn("empid", Type.INT4);
+    schema.addColumn("deptname", Type.TEXT);
+
+    Schema schema2 = new Schema();
+    schema2.addColumn("deptname", Type.TEXT);
+    schema2.addColumn("manager", Type.TEXT);
+
+    Schema schema3 = new Schema();
+    schema3.addColumn("deptname", Type.TEXT);
+    schema3.addColumn("score", Type.INT4);
+
+    TableMeta meta = CatalogUtil.newTableMeta("TEXT");
+    TableDesc people = new TableDesc(
+        CatalogUtil.buildFQName(TajoConstants.DEFAULT_DATABASE_NAME, "employee"), schema, meta,
+        CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(people);
+
+    TableDesc student = new TableDesc(
+        CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), schema2, "TEXT", new KeyValueSet(),
+        CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(student);
+
+    TableDesc score = new TableDesc(
+        CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), schema3, "TEXT", new KeyValueSet(),
+        CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(score);
+
+    FunctionDesc funcDesc = new FunctionDesc("sumtest", SumInt.class, FunctionType.AGGREGATION,
+        CatalogUtil.newSimpleDataType(Type.INT4),
+        CatalogUtil.newSimpleDataTypeArray(Type.INT4));
+
+
+    // TPC-H Schema for Complex Queries
+    String [] tpchTables = {
+        "part", "supplier", "partsupp", "nation", "region", "lineitem"
+    };
+    tpch = new TPCH();
+    tpch.loadSchemas();
+    tpch.loadOutSchema();
+    for (String table : tpchTables) {
+      TableMeta m = CatalogUtil.newTableMeta("TEXT");
+      TableDesc d = CatalogUtil.newTableDesc(
+          CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, table), tpch.getSchema(table), m,
+          CommonTestingUtil.getTestDir());
+      catalog.createTable(d);
+    }
+
+    catalog.createFunction(funcDesc);
+    sqlAnalyzer = new SQLAnalyzer();
+    planner = new LogicalPlanner(catalog, TablespaceManager.getInstance());
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    util.shutdownCatalogCluster();
+  }
+
+  static String[] QUERIES = {
+      "select name, empid, deptname from employee where empId > 500", // 0
+      "select name, empid, e.deptname, manager from employee as e, dept as dp", // 1
+      "select name, empid, e.deptname, manager, score from employee as e, dept, score", // 2
+      "select p.deptname, sumtest(score) from dept as p, score group by p.deptName having sumtest(score) > 30", // 3
+      "select p.deptname, score*200 from dept as p, score order by score*10 asc", // 4
+      "select name from employee where empId = 100", // 5
+      "select name, score from employee, score", // 6
+      "select p.deptName, sumtest(score) from dept as p, score group by p.deptName", // 7
+      "create table store1 as select p.deptName, sumtest(score) from dept as p, score group by p.deptName", // 8
+      "select deptName, sumtest(score) from score group by deptName having sumtest(score) > 30", // 9
+      "select 7 + 8 as res1, 8 * 9 as res2, 10 * 10 as res3", // 10
+      "create index idx_employee on employee using bitmap_idx (name null first, empId desc) where empid > 100", // 11
+      "select name, score from employee, score order by score limit 3", // 12
+      "select length(name), length(deptname), *, empid+10 from employee where empId > 500", // 13
+  };
+
+  private static QueryContext createQueryContext() {
+    QueryContext qc = new QueryContext(util.getConfiguration(), session);
+    qc.put(QueryVars.DEFAULT_SPACE_URI, "file:/");
+    qc.put(QueryVars.DEFAULT_SPACE_ROOT_URI, "file:/");
+    return qc;
+  }
+
+  public static final void testCloneLogicalNode(LogicalNode n1) throws CloneNotSupportedException {
+    LogicalNode copy = (LogicalNode) n1.clone();
+    assertTrue(n1.deepEquals(copy));
+  }
+
+  @Test
+  public final void testSingleRelation() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[0]);
+    LogicalPlan planNode = planner.createPlan(qc, expr);
+    LogicalNode plan = planNode.getRootBlock().getRoot();
+    assertEquals(NodeType.ROOT, plan.getType());
+    testCloneLogicalNode(plan);
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+
+    assertEquals(NodeType.SELECTION, projNode.getChild().getType());
+    SelectionNode selNode = projNode.getChild();
+
+    assertEquals(NodeType.SCAN, selNode.getChild().getType());
+    ScanNode scanNode = selNode.getChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), scanNode.getTableName());
+  }
+
+  public static void assertSchema(Schema expected, Schema schema) {
+    Column expectedColumn;
+    Column column;
+    for (int i = 0; i < expected.size(); i++) {
+      expectedColumn = expected.getColumn(i);
+      column = schema.getColumn(expectedColumn.getSimpleName());
+      assertEquals(expectedColumn.getSimpleName(), column.getSimpleName());
+      assertEquals(expectedColumn.getDataType(), column.getDataType());
+    }
+  }
+
+  @Test
+  public final void testImplicityJoinPlan() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    // two relations
+    Expr expr = sqlAnalyzer.parse(QUERIES[1]);
+    LogicalPlan planNode = planner.createPlan(qc, expr);
+    LogicalNode plan = planNode.getRootBlock().getRoot();
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+    testCloneLogicalNode(root);
+
+    Schema expectedSchema = new Schema();
+    expectedSchema.addColumn("name", Type.TEXT);
+    expectedSchema.addColumn("empid", Type.INT4);
+    expectedSchema.addColumn("deptname", Type.TEXT);
+    expectedSchema.addColumn("manager", Type.TEXT);
+    for (int i = 0; i < expectedSchema.size(); i++) {
+      Column found = root.getOutSchema().getColumn(expectedSchema.getColumn(i).getSimpleName());
+      assertEquals(expectedSchema.getColumn(i).getDataType(), found.getDataType());
+    }
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+
+    assertEquals(NodeType.JOIN, projNode.getChild().getType());
+    JoinNode joinNode = projNode.getChild();
+
+    assertEquals(NodeType.SCAN, joinNode.getLeftChild().getType());
+    ScanNode leftNode = joinNode.getLeftChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), leftNode.getTableName());
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode rightNode = joinNode.getRightChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), rightNode.getTableName());
+
+    // three relations
+    expr = sqlAnalyzer.parse(QUERIES[2]);
+    plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    testCloneLogicalNode(plan);
+
+    expectedSchema.addColumn("score", Type.INT4);
+    assertSchema(expectedSchema, plan.getOutSchema());
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    root = (LogicalRootNode) plan;
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    projNode = root.getChild();
+
+    assertEquals(NodeType.JOIN, projNode.getChild().getType());
+    joinNode = projNode.getChild();
+
+    assertEquals(NodeType.JOIN, joinNode.getLeftChild().getType());
+
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode scan1 = joinNode.getRightChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), scan1.getTableName());
+
+    JoinNode leftNode2 = joinNode.getLeftChild();
+    assertEquals(NodeType.JOIN, leftNode2.getType());
+
+    assertEquals(NodeType.SCAN, leftNode2.getLeftChild().getType());
+    ScanNode leftScan = leftNode2.getLeftChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), leftScan.getTableName());
+
+    assertEquals(NodeType.SCAN, leftNode2.getRightChild().getType());
+    ScanNode rightScan = leftNode2.getRightChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), rightScan.getTableName());
+  }
+
+
+
+  String [] JOINS = {
+      "select name, dept.deptName, score from employee natural join dept natural join score", // 0
+      "select name, dept.deptName, score from employee inner join dept on employee.deptName = dept.deptName inner join score on dept.deptName = score.deptName", // 1
+      "select name, dept.deptName, score from employee left outer join dept on employee.deptName = dept.deptName right outer join score on dept.deptName = score.deptName" // 2
+  };
+
+  static Schema expectedJoinSchema;
+  static {
+    expectedJoinSchema = new Schema();
+    expectedJoinSchema.addColumn("name", Type.TEXT);
+    expectedJoinSchema.addColumn("deptname", Type.TEXT);
+    expectedJoinSchema.addColumn("score", Type.INT4);
+  }
+
+  @Test
+  public final void testNaturalJoinPlan() throws TajoException {
+    QueryContext qc = createQueryContext();
+    // two relations
+    Expr context = sqlAnalyzer.parse(JOINS[0]);
+    LogicalNode plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertSchema(expectedJoinSchema, plan.getOutSchema());
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode proj = root.getChild();
+    assertEquals(NodeType.JOIN, proj.getChild().getType());
+    JoinNode join = proj.getChild();
+    assertEquals(JoinType.INNER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    assertTrue(join.hasJoinQual());
+    ScanNode scan = join.getRightChild();
+    assertEquals("default.score", scan.getTableName());
+
+    assertEquals(NodeType.JOIN, join.getLeftChild().getType());
+    join = join.getLeftChild();
+    assertEquals(JoinType.INNER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getLeftChild().getType());
+    ScanNode outer = join.getLeftChild();
+    assertEquals("default.employee", outer.getTableName());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    ScanNode inner = join.getRightChild();
+    assertEquals("default.dept", inner.getTableName());
+  }
+
+  @Test
+  public final void testInnerJoinPlan() throws TajoException {
+    QueryContext qc = createQueryContext();
+    // two relations
+    Expr expr = sqlAnalyzer.parse(JOINS[1]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    LogicalNode root = plan.getRootBlock().getRoot();
+    testJsonSerDerObject(root);
+    assertSchema(expectedJoinSchema, root.getOutSchema());
+
+    assertEquals(NodeType.ROOT, root.getType());
+    assertEquals(NodeType.PROJECTION, ((LogicalRootNode)root).getChild().getType());
+    ProjectionNode proj = ((LogicalRootNode)root).getChild();
+    assertEquals(NodeType.JOIN, proj.getChild().getType());
+    JoinNode join = proj.getChild();
+    assertEquals(JoinType.INNER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    ScanNode scan = join.getRightChild();
+    assertEquals("default.score", scan.getTableName());
+
+    assertEquals(NodeType.JOIN, join.getLeftChild().getType());
+    join = join.getLeftChild();
+    assertEquals(JoinType.INNER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getLeftChild().getType());
+    ScanNode outer = join.getLeftChild();
+    assertEquals("default.employee", outer.getTableName());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    ScanNode inner = join.getRightChild();
+    assertEquals("default.dept", inner.getTableName());
+    assertTrue(join.hasJoinQual());
+    assertEquals(EvalType.EQUAL, join.getJoinQual().getType());
+  }
+
+  @Test
+  public final void testOuterJoinPlan() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    // two relations
+    Expr expr = sqlAnalyzer.parse(JOINS[2]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertSchema(expectedJoinSchema, plan.getOutSchema());
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode proj = root.getChild();
+    assertEquals(NodeType.JOIN, proj.getChild().getType());
+    JoinNode join = proj.getChild();
+    assertEquals(JoinType.RIGHT_OUTER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    ScanNode scan = join.getRightChild();
+    assertEquals("default.score", scan.getTableName());
+
+    assertEquals(NodeType.JOIN, join.getLeftChild().getType());
+    join = join.getLeftChild();
+    assertEquals(JoinType.LEFT_OUTER, join.getJoinType());
+    assertEquals(NodeType.SCAN, join.getLeftChild().getType());
+    ScanNode outer = join.getLeftChild();
+    assertEquals("default.employee", outer.getTableName());
+    assertEquals(NodeType.SCAN, join.getRightChild().getType());
+    ScanNode inner = join.getRightChild();
+    assertEquals("default.dept", inner.getTableName());
+    assertTrue(join.hasJoinQual());
+    assertEquals(EvalType.EQUAL, join.getJoinQual().getType());
+  }
+
+
+  @Test
+  public final void testGroupby() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    // without 'having clause'
+    Expr context = sqlAnalyzer.parse(QUERIES[7]);
+    LogicalNode plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+    testQuery7(root.getChild());
+
+    // with having clause
+    context = sqlAnalyzer.parse(QUERIES[3]);
+    plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testCloneLogicalNode(plan);
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    root = (LogicalRootNode) plan;
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+    assertEquals(NodeType.HAVING, projNode.getChild().getType());
+    HavingNode havingNode = projNode.getChild();
+    assertEquals(NodeType.GROUP_BY, havingNode.getChild().getType());
+    GroupbyNode groupByNode =  havingNode.getChild();
+
+    assertEquals(NodeType.JOIN, groupByNode.getChild().getType());
+    JoinNode joinNode = groupByNode.getChild();
+
+    assertEquals(NodeType.SCAN, joinNode.getLeftChild().getType());
+    ScanNode leftNode = joinNode.getLeftChild();
+    assertEquals("default.dept", leftNode.getTableName());
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode rightNode = joinNode.getRightChild();
+    assertEquals("default.score", rightNode.getTableName());
+
+    //LogicalOptimizer.optimize(context, plan);
+  }
+
+
+  @Test
+  public final void testMultipleJoin() throws IOException, TajoException {
+    Expr expr = sqlAnalyzer.parse(
+        FileUtil.readTextFile(new File("src/test/resources/queries/TestJoinQuery/testTPCHQ2Join.sql")));
+    QueryContext qc = createQueryContext();
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    Schema expected = tpch.getOutSchema("q2");
+    assertSchema(expected, plan.getOutSchema());
+  }
+
+  private final void findJoinQual(EvalNode evalNode, Map<BinaryEval, Boolean> qualMap,
+                                  EvalType leftType, EvalType rightType)
+      throws IOException, TajoException {
+    Preconditions.checkArgument(evalNode instanceof BinaryEval);
+    BinaryEval qual = (BinaryEval)evalNode;
+
+    if (qual.getLeftExpr().getType() == leftType && qual.getRightExpr().getType() == rightType) {
+      assertEquals(qual.getLeftExpr().getType(), EvalType.FIELD);
+      FieldEval leftField = (FieldEval)qual.getLeftExpr();
+
+      for (Map.Entry<BinaryEval, Boolean> entry : qualMap.entrySet()) {
+        FieldEval leftJoinField = (FieldEval)entry.getKey().getLeftExpr();
+
+        if (qual.getRightExpr().getType() == entry.getKey().getRightExpr().getType()) {
+          if (rightType == EvalType.FIELD) {
+            FieldEval rightField = (FieldEval)qual.getRightExpr();
+            FieldEval rightJoinField = (FieldEval)entry.getKey().getRightExpr();
+
+            if (leftJoinField.getColumnRef().getQualifiedName().equals(leftField.getColumnRef().getQualifiedName())
+                && rightField.getColumnRef().getQualifiedName().equals(rightJoinField.getColumnRef().getQualifiedName())) {
+              qualMap.put(entry.getKey(), Boolean.TRUE);
+            }
+          } else if (rightType == EvalType.CONST) {
+            ConstEval rightField = (ConstEval)qual.getRightExpr();
+            ConstEval rightJoinField = (ConstEval)entry.getKey().getRightExpr();
+
+            if (leftJoinField.getColumnRef().getQualifiedName().equals(leftField.getColumnRef().getQualifiedName()) &&
+                rightField.getValue().equals(rightJoinField.getValue())) {
+              qualMap.put(entry.getKey(), Boolean.TRUE);
+            }
+          } else if (rightType == EvalType.ROW_CONSTANT) {
+            RowConstantEval rightField = qual.getRightExpr();
+            RowConstantEval rightJoinField = entry.getKey().getRightExpr();
+
+            if (leftJoinField.getColumnRef().getQualifiedName().equals(leftField.getColumnRef().getQualifiedName())) {
+              assertEquals(rightField.getValues().length, rightJoinField.getValues().length);
+              for (int i = 0; i < rightField.getValues().length; i++) {
+                assertEquals(rightField.getValues()[i], rightJoinField.getValues()[i]);
+              }
+              qualMap.put(entry.getKey(), Boolean.TRUE);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  @Test
+  public final void testJoinWithMultipleJoinQual1() throws IOException, TajoException {
+    Expr expr = sqlAnalyzer.parse(
+        FileUtil.readTextFile(new File
+            ("src/test/resources/queries/TestJoinQuery/testJoinWithMultipleJoinQual1.sql")));
+    QueryContext qc = createQueryContext();
+
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    LogicalNode node = plan.getRootBlock().getRoot();
+    testJsonSerDerObject(node);
+
+    Schema expected = tpch.getOutSchema("q2");
+    assertSchema(expected, node.getOutSchema());
+
+    LogicalOptimizer optimizer = new LogicalOptimizer(util.getConfiguration(), catalog);
+    optimizer.optimize(plan);
+
+    LogicalNode[] nodes = PlannerUtil.findAllNodes(node, NodeType.JOIN);
+    Map<BinaryEval, Boolean> qualMap = TUtil.newHashMap();
+    BinaryEval joinQual = new BinaryEval(EvalType.EQUAL
+        , new FieldEval(new Column("default.n.n_regionkey", Type.INT4))
+        , new FieldEval(new Column("default.ps.ps_suppkey", Type.INT4))
+        );
+    qualMap.put(joinQual, Boolean.FALSE);
+
+    for(LogicalNode eachNode : nodes) {
+      JoinNode joinNode = (JoinNode)eachNode;
+      EvalNode[] evalNodes = AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual());
+
+      for(EvalNode evalNode : evalNodes) {
+        findJoinQual(evalNode, qualMap, EvalType.FIELD, EvalType.FIELD);
+      }
+    }
+
+    for (Map.Entry<BinaryEval, Boolean> entry : qualMap.entrySet()) {
+      if (!entry.getValue()) {
+        Preconditions.checkArgument(false,
+            "JoinQual not found. -> required JoinQual:" + entry.getKey().toJson());
+      }
+    }
+  }
+
+  @Test
+  public final void testJoinWithMultipleJoinQual2() throws IOException, TajoException {
+    Expr expr = sqlAnalyzer.parse(
+        FileUtil.readTextFile(new File
+            ("src/test/resources/queries/TestJoinQuery/testJoinWithMultipleJoinQual2.sql")));
+    QueryContext qc = createQueryContext();
+
+    LogicalPlan plan = planner.createPlan(qc,expr);
+    LogicalNode node = plan.getRootBlock().getRoot();
+    testJsonSerDerObject(node);
+
+    LogicalOptimizer optimizer = new LogicalOptimizer(util.getConfiguration(), catalog);
+    optimizer.optimize(plan);
+
+    LogicalNode[] nodes = PlannerUtil.findAllNodes(node, NodeType.SCAN);
+    Map<BinaryEval, Boolean> qualMap = TUtil.newHashMap();
+    BinaryEval joinQual = new BinaryEval(EvalType.EQUAL
+        , new FieldEval(new Column("default.n.n_name", Type.TEXT))
+        , new ConstEval(new TextDatum("MOROCCO"))
+    );
+    qualMap.put(joinQual, Boolean.FALSE);
+
+    for(LogicalNode eachNode : nodes) {
+      ScanNode scanNode = (ScanNode)eachNode;
+      if (scanNode.hasQual()) {
+        EvalNode[] evalNodes = AlgebraicUtil.toConjunctiveNormalFormArray(scanNode.getQual());
+
+        for(EvalNode evalNode : evalNodes) {
+          findJoinQual(evalNode, qualMap, EvalType.FIELD, EvalType.CONST);
+        }
+      }
+    }
+
+    for (Map.Entry<BinaryEval, Boolean> entry : qualMap.entrySet()) {
+      if (!entry.getValue()) {
+        Preconditions.checkArgument(false,
+            "SelectionQual not found. -> required JoinQual:" + entry.getKey().toJson());
+      }
+    }
+  }
+
+  @Test
+  public final void testJoinWithMultipleJoinQual3() throws IOException, TajoException {
+    Expr expr = sqlAnalyzer.parse(
+        FileUtil.readTextFile(new File
+            ("src/test/resources/queries/TestJoinQuery/testJoinWithMultipleJoinQual3.sql")));
+    QueryContext qc = createQueryContext();
+
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    LogicalNode node = plan.getRootBlock().getRoot();
+    testJsonSerDerObject(node);
+
+    LogicalOptimizer optimizer = new LogicalOptimizer(util.getConfiguration(), catalog);
+    optimizer.optimize(plan);
+
+    LogicalNode[] nodes = PlannerUtil.findAllNodes(node, NodeType.SCAN);
+    Map<BinaryEval, Boolean> qualMap = TUtil.newHashMap();
+    TextDatum[] datums = new TextDatum[3];
+    datums[0] = new TextDatum("ARGENTINA");
+    datums[1] = new TextDatum("ETHIOPIA");
+    datums[2] = new TextDatum("MOROCCO");
+
+    BinaryEval joinQual = new BinaryEval(EvalType.EQUAL
+        , new FieldEval(new Column("default.n.n_name", Type.TEXT))
+        , new RowConstantEval(datums)
+    );
+    qualMap.put(joinQual, Boolean.FALSE);
+
+    for(LogicalNode eachNode : nodes) {
+      ScanNode scanNode = (ScanNode)eachNode;
+      if (scanNode.hasQual()) {
+        EvalNode[] evalNodes = AlgebraicUtil.toConjunctiveNormalFormArray(scanNode.getQual());
+
+        for(EvalNode evalNode : evalNodes) {
+          findJoinQual(evalNode, qualMap, EvalType.FIELD, EvalType.ROW_CONSTANT);
+        }
+      }
+    }
+
+    for (Map.Entry<BinaryEval, Boolean> entry : qualMap.entrySet()) {
+      if (!entry.getValue()) {
+        Preconditions.checkArgument(false,
+            "ScanQual not found. -> required JoinQual:" + entry.getKey().toJson());
+      }
+    }
+  }
+
+
+  @Test
+  public final void testJoinWithMultipleJoinQual4() throws IOException, TajoException {
+    Expr expr = sqlAnalyzer.parse(
+        FileUtil.readTextFile(new File
+            ("src/test/resources/queries/TestJoinQuery/testJoinWithMultipleJoinQual4.sql")));
+    QueryContext qc = createQueryContext();
+
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    LogicalNode node = plan.getRootBlock().getRoot();
+    testJsonSerDerObject(node);
+
+    LogicalOptimizer optimizer = new LogicalOptimizer(util.getConfiguration(), catalog);
+    optimizer.optimize(plan);
+
+    Map<BinaryEval, Boolean> scanMap = TUtil.newHashMap();
+    TextDatum[] datums = new TextDatum[3];
+    datums[0] = new TextDatum("ARGENTINA");
+    datums[1] = new TextDatum("ETHIOPIA");
+    datums[2] = new TextDatum("MOROCCO");
+
+    BinaryEval scanQual = new BinaryEval(EvalType.EQUAL
+        , new FieldEval(new Column("default.n.n_name", Type.TEXT))
+        , new RowConstantEval(datums)
+    );
+    scanMap.put(scanQual, Boolean.FALSE);
+
+    Map<BinaryEval, Boolean> joinQualMap = TUtil.newHashMap();
+    BinaryEval joinQual = new BinaryEval(EvalType.GTH
+        , new FieldEval(new Column("default.t.n_nationkey", Type.INT4))
+        , new FieldEval(new Column("default.s.s_suppkey", Type.INT4))
+    );
+
+    /* following code is commented because theta join is not supported yet
+     * TODO It SHOULD be restored after TAJO-742 is resolved. */
+    //joinQualMap.put(joinQual, Boolean.FALSE);
+
+    LogicalNode[] nodes = PlannerUtil.findAllNodes(node, NodeType.JOIN);
+    for(LogicalNode eachNode : nodes) {
+      JoinNode joinNode = (JoinNode)eachNode;
+      if (joinNode.hasJoinQual()) {
+        EvalNode[] evalNodes = AlgebraicUtil.toConjunctiveNormalFormArray(joinNode.getJoinQual());
+
+        for(EvalNode evalNode : evalNodes) {
+          findJoinQual(evalNode, joinQualMap, EvalType.FIELD, EvalType.FIELD);
+        }
+      }
+    }
+
+    nodes = PlannerUtil.findAllNodes(node, NodeType.SCAN);
+    for(LogicalNode eachNode : nodes) {
+      ScanNode scanNode = (ScanNode)eachNode;
+      if (scanNode.hasQual()) {
+        EvalNode[] evalNodes = AlgebraicUtil.toConjunctiveNormalFormArray(scanNode.getQual());
+
+        for(EvalNode evalNode : evalNodes) {
+          findJoinQual(evalNode, scanMap, EvalType.FIELD, EvalType.ROW_CONSTANT);
+        }
+      }
+    }
+
+
+    for (Map.Entry<BinaryEval, Boolean> entry : joinQualMap.entrySet()) {
+      if (!entry.getValue()) {
+        Preconditions.checkArgument(false,
+            "JoinQual not found. -> required JoinQual:" + entry.getKey().toJson());
+      }
+    }
+
+    for (Map.Entry<BinaryEval, Boolean> entry : scanMap.entrySet()) {
+      if (!entry.getValue()) {
+        Preconditions.checkArgument(false,
+            "ScanQual not found. -> required JoinQual:" + entry.getKey().toJson());
+      }
+    }
+  }
+
+  static void testQuery7(LogicalNode plan) {
+    assertEquals(NodeType.PROJECTION, plan.getType());
+    ProjectionNode projNode = (ProjectionNode) plan;
+    assertEquals(NodeType.GROUP_BY, projNode.getChild().getType());
+    GroupbyNode groupByNode = projNode.getChild();
+
+    assertEquals(NodeType.JOIN, groupByNode.getChild().getType());
+    JoinNode joinNode = groupByNode.getChild();
+
+    assertEquals(NodeType.SCAN, joinNode.getLeftChild().getType());
+    ScanNode leftNode = joinNode.getLeftChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), leftNode.getTableName());
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode rightNode = joinNode.getRightChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), rightNode.getTableName());
+  }
+
+
+  @Test
+  public final void testStoreTable() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr context = sqlAnalyzer.parse(QUERIES[8]);
+
+    LogicalNode plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testCloneLogicalNode(plan);
+    testJsonSerDerObject(plan);
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+
+    assertEquals(NodeType.CREATE_TABLE, root.getChild().getType());
+    StoreTableNode storeNode = root.getChild();
+    testQuery7(storeNode.getChild());
+  }
+
+  @Test
+  public final void testOrderBy() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[4]);
+
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    testCloneLogicalNode(plan);
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+
+    assertEquals(NodeType.SORT, projNode.getChild().getType());
+    SortNode sortNode = projNode.getChild();
+
+    assertEquals(NodeType.JOIN, sortNode.getChild().getType());
+    JoinNode joinNode = sortNode.getChild();
+
+    assertEquals(NodeType.SCAN, joinNode.getLeftChild().getType());
+    ScanNode leftNode = joinNode.getLeftChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), leftNode.getTableName());
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode rightNode = joinNode.getRightChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), rightNode.getTableName());
+  }
+
+  @Test
+  public final void testLimit() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[12]);
+
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    testCloneLogicalNode(plan);
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+
+    assertEquals(NodeType.LIMIT, projNode.getChild().getType());
+    LimitNode limitNode = projNode.getChild();
+
+    assertEquals(NodeType.SORT, limitNode.getChild().getType());
+  }
+
+  @Test
+  public final void testSPJPush() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[5]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    testCloneLogicalNode(plan);
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+    assertEquals(NodeType.SELECTION, projNode.getChild().getType());
+    SelectionNode selNode = projNode.getChild();
+    assertEquals(NodeType.SCAN, selNode.getChild().getType());
+    ScanNode scanNode = selNode.getChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), scanNode.getTableName());
+  }
+
+
+
+  @Test
+  public final void testSPJ() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[6]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    testCloneLogicalNode(plan);
+  }
+
+  @Test
+  public final void testJson() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+	  Expr expr = sqlAnalyzer.parse(QUERIES[9]);
+	  LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+
+	  String json = plan.toJson();
+	  LogicalNode fromJson = CoreGsonHelper.fromJson(json, LogicalNode.class);
+	  assertEquals(NodeType.ROOT, fromJson.getType());
+	  LogicalNode project = ((LogicalRootNode)fromJson).getChild();
+	  assertEquals(NodeType.PROJECTION, project.getType());
+	  assertEquals(NodeType.HAVING, ((ProjectionNode) project).getChild().getType());
+    HavingNode havingNode = ((ProjectionNode) project).getChild();
+    assertEquals(NodeType.GROUP_BY, havingNode.getChild().getType());
+    GroupbyNode groupbyNode = havingNode.getChild();
+    assertEquals(NodeType.SCAN, groupbyNode.getChild().getType());
+	  LogicalNode scan = groupbyNode.getChild();
+	  assertEquals(NodeType.SCAN, scan.getType());
+  }
+
+  @Test
+  public final void testVisitor() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    // two relations
+    Expr expr = sqlAnalyzer.parse(QUERIES[1]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+
+    TestVisitor vis = new TestVisitor();
+    plan.postOrder(vis);
+
+    assertEquals(NodeType.ROOT, vis.stack.pop().getType());
+    assertEquals(NodeType.PROJECTION, vis.stack.pop().getType());
+    assertEquals(NodeType.JOIN, vis.stack.pop().getType());
+    assertEquals(NodeType.SCAN, vis.stack.pop().getType());
+    assertEquals(NodeType.SCAN, vis.stack.pop().getType());
+  }
+
+  private static class TestVisitor implements LogicalNodeVisitor {
+    Stack<LogicalNode> stack = new Stack<LogicalNode>();
+    @Override
+    public void visit(LogicalNode node) {
+      stack.push(node);
+    }
+  }
+
+
+  @Test
+  public final void testExprNode() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[10]);
+    LogicalPlan rootNode = planner.createPlan(qc, expr);
+    LogicalNode plan = rootNode.getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.EXPRS, root.getChild().getType());
+    Schema out = root.getOutSchema();
+
+    Iterator<Column> it = out.getRootColumns().iterator();
+    Column col = it.next();
+    assertEquals("res1", col.getSimpleName());
+    col = it.next();
+    assertEquals("res2", col.getSimpleName());
+    col = it.next();
+    assertEquals("res3", col.getSimpleName());
+  }
+
+  @Test
+  public final void testCreateIndexNode() throws TajoException {
+    QueryContext qc = new QueryContext(util.getConfiguration(), session);
+    Expr expr = sqlAnalyzer.parse(QUERIES[11]);
+    LogicalPlan rootNode = planner.createPlan(qc, expr);
+    LogicalNode plan = rootNode.getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.CREATE_INDEX, root.getChild().getType());
+    CreateIndexNode createIndexNode = root.getChild();
+
+    assertEquals(NodeType.PROJECTION, createIndexNode.getChild().getType());
+    ProjectionNode projNode = createIndexNode.getChild();
+
+    assertEquals(NodeType.SELECTION, projNode.getChild().getType());
+    SelectionNode selNode = projNode.getChild();
+
+    assertEquals(NodeType.SCAN, selNode.getChild().getType());
+    ScanNode scanNode = selNode.getChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), scanNode.getTableName());
+  }
+
+  @Test
+  public final void testAsterisk() throws CloneNotSupportedException, TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(QUERIES[13]);
+    LogicalPlan planNode = planner.createPlan(qc, expr);
+    LogicalNode plan = planNode.getRootBlock().getRoot();
+    assertEquals(NodeType.ROOT, plan.getType());
+    testCloneLogicalNode(plan);
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+    assertEquals(6, projNode.getOutSchema().size());
+
+    assertEquals(NodeType.SELECTION, projNode.getChild().getType());
+    SelectionNode selNode = projNode.getChild();
+
+    assertEquals(NodeType.SCAN, selNode.getChild().getType());
+    ScanNode scanNode = selNode.getChild();
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), scanNode.getTableName());
+  }
+
+  static final String ALIAS [] = {
+    "select deptName, sum(score) as total from score group by deptName",
+    "select em.empId as id, sum(score) as total from employee as em inner join score using (em.deptName) group by id"
+  };
+
+
+  @Test
+  public final void testAlias1() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(ALIAS[0]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+
+    Schema finalSchema = root.getOutSchema();
+    Iterator<Column> it = finalSchema.getRootColumns().iterator();
+    Column col = it.next();
+    assertEquals("deptname", col.getSimpleName());
+    col = it.next();
+    assertEquals("total", col.getSimpleName());
+
+    expr = sqlAnalyzer.parse(ALIAS[1]);
+    plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    root = (LogicalRootNode) plan;
+
+    finalSchema = root.getOutSchema();
+    it = finalSchema.getRootColumns().iterator();
+    col = it.next();
+    assertEquals("id", col.getSimpleName());
+    col = it.next();
+    assertEquals("total", col.getSimpleName());
+  }
+
+  @Test
+  public final void testAlias2() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(ALIAS[1]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+
+    Schema finalSchema = root.getOutSchema();
+    Iterator<Column> it = finalSchema.getRootColumns().iterator();
+    Column col = it.next();
+    assertEquals("id", col.getSimpleName());
+    col = it.next();
+    assertEquals("total", col.getSimpleName());
+  }
+
+  static final String CREATE_TABLE [] = {
+    "create external table table1 (name text, age int, earn bigint, score real) using csv with ('csv.delimiter'='|') location '/tmp/data'"
+  };
+
+  @Test
+  public final void testCreateTableDef() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(CREATE_TABLE[0]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    LogicalRootNode root = (LogicalRootNode) plan;
+    testJsonSerDerObject(root);
+    assertEquals(NodeType.CREATE_TABLE, root.getChild().getType());
+    CreateTableNode createTable = root.getChild();
+
+    Schema def = createTable.getTableSchema();
+    assertEquals("name", def.getColumn(0).getSimpleName());
+    assertEquals(Type.TEXT, def.getColumn(0).getDataType().getType());
+    assertEquals("age", def.getColumn(1).getSimpleName());
+    assertEquals(Type.INT4, def.getColumn(1).getDataType().getType());
+    assertEquals("earn", def.getColumn(2).getSimpleName());
+    assertEquals(Type.INT8, def.getColumn(2).getDataType().getType());
+    assertEquals("score", def.getColumn(3).getSimpleName());
+    assertEquals(Type.FLOAT4, def.getColumn(3).getDataType().getType());
+    assertTrue("TEXT".equalsIgnoreCase(createTable.getStorageType()));
+    assertEquals("file://tmp/data", createTable.getUri().toString());
+    assertTrue(createTable.hasOptions());
+    assertEquals("|", createTable.getOptions().get("csv.delimiter"));
+  }
+
+  private static final List<Set<Column>> testGenerateCuboidsResult
+    = Lists.newArrayList();
+  private static final int numCubeColumns = 3;
+  private static final Column [] testGenerateCuboids = new Column[numCubeColumns];
+
+  private static final List<Set<Column>> testCubeByResult
+    = Lists.newArrayList();
+  private static final Column [] testCubeByCuboids = new Column[2];
+  static {
+    testGenerateCuboids[0] = new Column("col1", Type.INT4);
+    testGenerateCuboids[1] = new Column("col2", Type.INT8);
+    testGenerateCuboids[2] = new Column("col3", Type.FLOAT4);
+
+    testGenerateCuboidsResult.add(new HashSet<Column>());
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[0]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[1]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[2]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[0],
+        testGenerateCuboids[1]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[0],
+        testGenerateCuboids[2]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[1],
+        testGenerateCuboids[2]));
+    testGenerateCuboidsResult.add(Sets.newHashSet(testGenerateCuboids[0],
+        testGenerateCuboids[1], testGenerateCuboids[2]));
+
+    testCubeByCuboids[0] = new Column("employee.name", Type.TEXT);
+    testCubeByCuboids[1] = new Column("employee.empid", Type.INT4);
+    testCubeByResult.add(new HashSet<Column>());
+    testCubeByResult.add(Sets.newHashSet(testCubeByCuboids[0]));
+    testCubeByResult.add(Sets.newHashSet(testCubeByCuboids[1]));
+    testCubeByResult.add(Sets.newHashSet(testCubeByCuboids[0],
+        testCubeByCuboids[1]));
+  }
+
+  @Test
+  public final void testGenerateCuboids() {
+    Column [] columns = new Column[3];
+
+    columns[0] = new Column("col1", Type.INT4);
+    columns[1] = new Column("col2", Type.INT8);
+    columns[2] = new Column("col3", Type.FLOAT4);
+
+    List<Column[]> cube = LogicalPlanner.generateCuboids(columns);
+    assertEquals(((int)Math.pow(2, numCubeColumns)), cube.size());
+
+    Set<Set<Column>> cuboids = Sets.newHashSet();
+    for (Column [] cols : cube) {
+      cuboids.add(Sets.newHashSet(cols));
+    }
+
+    for (Set<Column> result : testGenerateCuboidsResult) {
+      assertTrue(cuboids.contains(result));
+    }
+  }
+
+  static final String setStatements [] = {
+    "select deptName from employee where deptName like 'data%' union all select deptName from score where deptName like 'data%'",
+  };
+
+  @Test
+  public final void testSetPlan() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(setStatements[0]);
+    LogicalNode plan = planner.createPlan(qc, expr).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.UNION, root.getChild().getType());
+    UnionNode union = root.getChild();
+    assertEquals(NodeType.PROJECTION, union.getLeftChild().getType());
+    assertEquals(NodeType.PROJECTION, union.getRightChild().getType());
+  }
+
+  static final String [] setQualifiers = {
+    "select name, empid from employee",
+    "select distinct name, empid from employee",
+    "select all name, empid from employee",
+  };
+
+  @Test
+  public void testSetQualifier() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr context = sqlAnalyzer.parse(setQualifiers[0]);
+    LogicalNode plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projectionNode = root.getChild();
+    assertEquals(NodeType.SCAN, projectionNode.getChild().getType());
+
+    context = sqlAnalyzer.parse(setQualifiers[1]);
+    plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    projectionNode = root.getChild();
+    assertEquals(NodeType.GROUP_BY, projectionNode.getChild().getType());
+
+    context = sqlAnalyzer.parse(setQualifiers[2]);
+    plan = planner.createPlan(qc, context).getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    root = (LogicalRootNode) plan;
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    projectionNode = root.getChild();
+    assertEquals(NodeType.SCAN, projectionNode.getChild().getType());
+  }
+
+  public void testJsonSerDerObject(LogicalNode rootNode) {
+    String json = rootNode.toJson();
+    LogicalNode fromJson = CoreGsonHelper.fromJson(json, LogicalNode.class);
+    assertTrue("JSON (de) serialization equivalence check", rootNode.deepEquals(fromJson));
+  }
+
+  // Table descriptions
+  //
+  // employee (name text, empid int4, deptname text)
+  // dept (deptname text, manager text)
+  // score (deptname text, score inet4)
+
+  static final String [] insertStatements = {
+      "insert into score select name from employee",                        // 0
+      "insert into score select name, empid from employee",                 // 1
+      "insert into employee (name, deptname) select * from dept",           // 2
+      "insert into location '/tmp/data' select name, empid from employee",  // 3
+      "insert overwrite into employee (name, deptname) select * from dept", // 4
+      "insert overwrite into LOCATION '/tmp/data' select * from dept",      // 5
+      "insert into employee (deptname, name) select deptname, manager from dept"  // 6
+  };
+
+  @Test
+  public final void testInsertInto0() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[0]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertFalse(insertNode.isOverwrite());
+    assertTrue(insertNode.hasTargetTable());
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), insertNode.getTableName());
+  }
+
+  @Test
+  public final void testInsertInto1() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[1]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertFalse(insertNode.isOverwrite());
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), insertNode.getTableName());
+  }
+
+  @Test
+  public final void testInsertInto2() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[2]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertFalse(insertNode.isOverwrite());
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), insertNode.getTableName());
+    assertTrue(insertNode.hasTargetSchema());
+    assertEquals(insertNode.getTargetSchema().getColumn(0).getSimpleName(), "name");
+    assertEquals(insertNode.getTargetSchema().getColumn(1).getSimpleName(), "deptname");
+  }
+
+  @Test
+  public final void testInsertInto3() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[3]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertFalse(insertNode.isOverwrite());
+    assertTrue(insertNode.hasUri());
+  }
+
+  @Test
+  public final void testInsertInto4() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[4]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertTrue(insertNode.isOverwrite());
+    assertTrue(insertNode.hasTargetTable());
+    assertEquals(CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "employee"), insertNode.getTableName());
+    assertTrue(insertNode.hasTargetSchema());
+    assertEquals(insertNode.getTargetSchema().getColumn(0).getSimpleName(), "name");
+    assertEquals(insertNode.getTargetSchema().getColumn(1).getSimpleName(), "deptname");
+  }
+
+  @Test
+  public final void testInsertInto5() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[5]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+    assertTrue(insertNode.isOverwrite());
+    assertTrue(insertNode.hasUri());
+  }
+
+  @Test
+  public final void testInsertInto6() throws TajoException {
+    QueryContext qc = createQueryContext();
+
+    Expr expr = sqlAnalyzer.parse(insertStatements[6]);
+    LogicalPlan plan = planner.createPlan(qc, expr);
+    assertEquals(1, plan.getQueryBlocks().size());
+    InsertNode insertNode = getInsertNode(plan);
+
+    ProjectionNode subquery = insertNode.getChild();
+    Target[] targets = subquery.getTargets();
+    // targets MUST be manager, NULL as empid, deptname
+    assertEquals(targets[0].getNamedColumn().getQualifiedName(), "default.dept.manager");
+    assertEquals(targets[1].getAlias(), "empid");
+    assertEquals(targets[1].getEvalTree().getType(), EvalType.CONST);
+    assertEquals(targets[2].getNamedColumn().getQualifiedName(), "default.dept.deptname");
+  }
+
+  private static InsertNode getInsertNode(LogicalPlan plan) {
+    LogicalRootNode root = plan.getRootBlock().getRoot();
+    assertEquals(NodeType.INSERT, root.getChild().getType());
+    return root.getChild();
+  }
+
+  String [] ALTER_PARTITIONS = {
+    "ALTER TABLE partitioned_table ADD PARTITION (col1 = 1 , col2 = 2) LOCATION 'hdfs://xxx" +
+      ".com/warehouse/partitioned_table/col1=1/col2=2'", //0
+    "ALTER TABLE partitioned_table DROP PARTITION (col1 = '2015' , col2 = '01', col3 = '11' )", //1
+  };
+
+  @Test
+  public final void testAddPartitionAndDropPartition() throws TajoException {
+    String tableName = CatalogUtil.normalizeIdentifier("partitioned_table");
+    String qualifiedTableName = CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, tableName);
+
+    Schema schema = new Schema();
+    schema.addColumn("id", Type.INT4)
+      .addColumn("name", Type.TEXT)
+      .addColumn("age", Type.INT4)
+      .addColumn("score", Type.FLOAT8);
+
+    KeyValueSet opts = new KeyValueSet();
+    opts.set("file.delimiter", ",");
+
+    Schema partSchema = new Schema();
+    partSchema.addColumn("id", Type.INT4);
+    partSchema.addColumn("name", Type.TEXT);
+
+    PartitionMethodDesc partitionMethodDesc =
+      new PartitionMethodDesc(DEFAULT_DATABASE_NAME, tableName,
+        CatalogProtos.PartitionType.COLUMN, "id,name", partSchema);
+
+    TableDesc desc = null;
+
+    try {
+      desc = new TableDesc(qualifiedTableName, schema, "TEXT", new KeyValueSet(),
+        CommonTestingUtil.getTestDir().toUri());
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+
+    desc.setPartitionMethod(partitionMethodDesc);
+    assertFalse(catalog.existsTable(qualifiedTableName));
+    catalog.createTable(desc);
+    assertTrue(catalog.existsTable(qualifiedTableName));
+
+    TableDesc retrieved = catalog.getTableDesc(qualifiedTableName);
+    assertEquals(retrieved.getName(), qualifiedTableName);
+    assertEquals(retrieved.getPartitionMethod().getPartitionType(), CatalogProtos.PartitionType.COLUMN);
+    assertEquals(retrieved.getPartitionMethod().getExpressionSchema().getColumn(0).getSimpleName(), "id");
+
+    QueryContext qc = new QueryContext(util.getConfiguration(), session);
+
+    // Testing alter table add partition
+    Expr expr = sqlAnalyzer.parse(ALTER_PARTITIONS[0]);
+    LogicalPlan rootNode = planner.createPlan(qc, expr);
+    LogicalNode plan = rootNode.getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    assertEquals(NodeType.ALTER_TABLE, root.getChild().getType());
+
+    AlterTableNode alterTableNode = root.getChild();
+
+    assertEquals(alterTableNode.getAlterTableOpType(), AlterTableOpType.ADD_PARTITION);
+
+    assertEquals(alterTableNode.getPartitionColumns().length, 2);
+    assertEquals(alterTableNode.getPartitionValues().length, 2);
+
+    assertEquals(alterTableNode.getPartitionColumns()[0], "col1");
+    assertEquals(alterTableNode.getPartitionColumns()[1], "col2");
+
+    assertEquals(alterTableNode.getPartitionValues()[0], "1");
+    assertEquals(alterTableNode.getPartitionValues()[1], "2");
+
+    assertEquals(alterTableNode.getLocation(), "hdfs://xxx.com/warehouse/partitioned_table/col1=1/col2=2");
+
+    // Testing alter table drop partition
+    expr = sqlAnalyzer.parse(ALTER_PARTITIONS[1]);
+    rootNode = planner.createPlan(qc, expr);
+    plan = rootNode.getRootBlock().getRoot();
+    testJsonSerDerObject(plan);
+    assertEquals(NodeType.ROOT, plan.getType());
+    root = (LogicalRootNode) plan;
+    assertEquals(NodeType.ALTER_TABLE, root.getChild().getType());
+
+    alterTableNode = root.getChild();
+
+    assertEquals(alterTableNode.getAlterTableOpType(), AlterTableOpType.DROP_PARTITION);
+
+    assertEquals(alterTableNode.getPartitionColumns().length, 3);
+    assertEquals(alterTableNode.getPartitionValues().length, 3);
+
+    assertEquals(alterTableNode.getPartitionColumns()[0], "col1");
+    assertEquals(alterTableNode.getPartitionColumns()[1], "col2");
+    assertEquals(alterTableNode.getPartitionColumns()[2], "col3");
+
+    assertEquals(alterTableNode.getPartitionValues()[0], "2015");
+    assertEquals(alterTableNode.getPartitionValues()[1], "01");
+    assertEquals(alterTableNode.getPartitionValues()[2], "11");
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/a4106883/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestPlannerUtil.java
----------------------------------------------------------------------
diff --git a/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestPlannerUtil.java b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestPlannerUtil.java
new file mode 100644
index 0000000..6c4d82d
--- /dev/null
+++ b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestPlannerUtil.java
@@ -0,0 +1,387 @@
+/**
+ * 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.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.LocatedFileStatus;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.fs.RemoteIterator;
+import org.apache.tajo.LocalTajoTestingUtility;
+import org.apache.tajo.TajoConstants;
+import org.apache.tajo.TajoTestingCluster;
+import org.apache.tajo.algebra.Expr;
+import org.apache.tajo.catalog.*;
+import org.apache.tajo.catalog.proto.CatalogProtos.FragmentProto;
+import org.apache.tajo.catalog.proto.CatalogProtos.FunctionType;
+import org.apache.tajo.common.TajoDataTypes.Type;
+import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.engine.function.builtin.SumInt;
+import org.apache.tajo.engine.parser.SQLAnalyzer;
+import org.apache.tajo.engine.planner.physical.PhysicalPlanUtil;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.LogicalPlanner;
+import org.apache.tajo.plan.expr.*;
+import org.apache.tajo.plan.logical.*;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.storage.TablespaceManager;
+import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.storage.TupleComparator;
+import org.apache.tajo.storage.VTuple;
+import org.apache.tajo.storage.fragment.FileFragment;
+import org.apache.tajo.storage.fragment.FragmentConvertor;
+import org.apache.tajo.util.CommonTestingUtil;
+import org.apache.tajo.util.KeyValueSet;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.apache.tajo.TajoConstants.DEFAULT_DATABASE_NAME;
+import static org.apache.tajo.TajoConstants.DEFAULT_TABLESPACE_NAME;
+import static org.junit.Assert.*;
+
+public class TestPlannerUtil {
+  private static TajoTestingCluster util;
+  private static CatalogService catalog;
+  private static SQLAnalyzer analyzer;
+  private static LogicalPlanner planner;
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    util = new TajoTestingCluster();
+    util.startCatalogCluster();
+    catalog = util.getMiniCatalogCluster().getCatalog();
+    catalog.createTablespace(DEFAULT_TABLESPACE_NAME, "hdfs://localhost:1234/warehouse");
+    catalog.createDatabase(DEFAULT_DATABASE_NAME, DEFAULT_TABLESPACE_NAME);
+
+    Schema schema = new Schema();
+    schema.addColumn("name", Type.TEXT);
+    schema.addColumn("empid", CatalogUtil.newSimpleDataType(Type.INT4));
+    schema.addColumn("deptname", Type.TEXT);
+
+    Schema schema2 = new Schema();
+    schema2.addColumn("deptname", Type.TEXT);
+    schema2.addColumn("manager", Type.TEXT);
+
+    Schema schema3 = new Schema();
+    schema3.addColumn("deptname", Type.TEXT);
+    schema3.addColumn("score", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    TableMeta meta = CatalogUtil.newTableMeta("TEXT");
+    TableDesc people = new TableDesc(
+        CatalogUtil.buildFQName(TajoConstants.DEFAULT_DATABASE_NAME, "employee"), schema, meta,
+        CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(people);
+
+    TableDesc student =
+        new TableDesc(
+            CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dept"), schema2, "TEXT",
+            new KeyValueSet(), CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(student);
+
+    TableDesc score =
+        new TableDesc(
+            CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "score"), schema3, "TEXT",
+            new KeyValueSet(), CommonTestingUtil.getTestDir().toUri());
+    catalog.createTable(score);
+
+    FunctionDesc funcDesc = new FunctionDesc("sumtest", SumInt.class, FunctionType.AGGREGATION,
+        CatalogUtil.newSimpleDataType(Type.INT4),
+        CatalogUtil.newSimpleDataTypeArray(Type.INT4));
+
+    catalog.createFunction(funcDesc);
+    analyzer = new SQLAnalyzer();
+    planner = new LogicalPlanner(catalog, TablespaceManager.getInstance());
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    util.shutdownCatalogCluster();
+  }
+
+  @Test
+  public final void testFindTopNode() throws CloneNotSupportedException, TajoException {
+    // two relations
+    Expr expr = analyzer.parse(TestLogicalPlanner.QUERIES[1]);
+    LogicalNode plan = planner.createPlan(LocalTajoTestingUtility.createDummyContext(util.getConfiguration()),
+        expr).getRootBlock().getRoot();
+
+    assertEquals(NodeType.ROOT, plan.getType());
+    LogicalRootNode root = (LogicalRootNode) plan;
+    TestLogicalPlanner.testCloneLogicalNode(root);
+
+    assertEquals(NodeType.PROJECTION, root.getChild().getType());
+    ProjectionNode projNode = root.getChild();
+
+    assertEquals(NodeType.JOIN, projNode.getChild().getType());
+    JoinNode joinNode = projNode.getChild();
+
+    assertEquals(NodeType.SCAN, joinNode.getLeftChild().getType());
+    ScanNode leftNode = joinNode.getLeftChild();
+    assertEquals("default.employee", leftNode.getTableName());
+    assertEquals(NodeType.SCAN, joinNode.getRightChild().getType());
+    ScanNode rightNode = joinNode.getRightChild();
+    assertEquals("default.dept", rightNode.getTableName());
+    
+    LogicalNode node = PlannerUtil.findTopNode(root, NodeType.ROOT);
+    assertEquals(NodeType.ROOT, node.getType());
+    
+    node = PlannerUtil.findTopNode(root, NodeType.PROJECTION);
+    assertEquals(NodeType.PROJECTION, node.getType());
+    
+    node = PlannerUtil.findTopNode(root, NodeType.JOIN);
+    assertEquals(NodeType.JOIN, node.getType());
+    
+    node = PlannerUtil.findTopNode(root, NodeType.SCAN);
+    assertEquals(NodeType.SCAN, node.getType());
+  }
+
+  @Test
+  public final void testIsJoinQual() {
+    FieldEval f1 = new FieldEval("part.p_partkey", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f2 = new FieldEval("partsupp.ps_partkey",
+        CatalogUtil.newSimpleDataType(Type.INT4));
+
+
+    BinaryEval [] joinQuals = new BinaryEval[5];
+    int idx = 0;
+    joinQuals[idx++] = new BinaryEval(EvalType.EQUAL, f1, f2);
+    joinQuals[idx++] = new BinaryEval(EvalType.LEQ, f1, f2);
+    joinQuals[idx++] = new BinaryEval(EvalType.LTH, f1, f2);
+    joinQuals[idx++] = new BinaryEval(EvalType.GEQ, f1, f2);
+    joinQuals[idx] = new BinaryEval(EvalType.GTH, f1, f2);
+    for (int i = 0; i < idx; i++) {
+      assertTrue(EvalTreeUtil.isJoinQual(joinQuals[idx], true));
+    }
+
+    BinaryEval [] wrongJoinQuals = new BinaryEval[5];
+    idx = 0;
+    wrongJoinQuals[idx++] = new BinaryEval(EvalType.OR, f1, f2);
+    wrongJoinQuals[idx++] = new BinaryEval(EvalType.PLUS, f1, f2);
+    wrongJoinQuals[idx++] = new BinaryEval(EvalType.LIKE, f1, f2);
+
+    ConstEval f3 = new ConstEval(DatumFactory.createInt4(1));
+    wrongJoinQuals[idx] = new BinaryEval(EvalType.EQUAL, f1, f3);
+
+    for (int i = 0; i < idx; i++) {
+      assertFalse(EvalTreeUtil.isJoinQual(wrongJoinQuals[idx], true));
+    }
+  }
+
+  @Test
+  public final void testGetJoinKeyPairs() {
+    Schema outerSchema = new Schema();
+    outerSchema.addColumn("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    outerSchema.addColumn("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    Schema innerSchema = new Schema();
+    innerSchema.addColumn("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    innerSchema.addColumn("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    FieldEval f1 = new FieldEval("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f2 = new FieldEval("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f3 = new FieldEval("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f4 = new FieldEval("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    EvalNode equiJoinQual = new BinaryEval(EvalType.EQUAL, f1, f2);
+
+    // the case where part is the outer and partsupp is the inner.
+    List<Column[]> pairs = PlannerUtil.getJoinKeyPairs(equiJoinQual, outerSchema,  innerSchema, false);
+    assertEquals(1, pairs.size());
+    assertEquals("employee.id1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("people.fid1", pairs.get(0)[1].getQualifiedName());
+
+    // after exchange of outer and inner
+    pairs = PlannerUtil.getJoinKeyPairs(equiJoinQual, innerSchema, outerSchema, false);
+    assertEquals("people.fid1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("employee.id1", pairs.get(0)[1].getQualifiedName());
+
+    // composited join key test
+    EvalNode joinQual2 = new BinaryEval(EvalType.EQUAL, f3, f4);
+    EvalNode compositedJoinQual = new BinaryEval(EvalType.AND, equiJoinQual, joinQual2);
+    pairs = PlannerUtil.getJoinKeyPairs(compositedJoinQual, outerSchema,  innerSchema, false);
+    assertEquals(2, pairs.size());
+    assertEquals("employee.id1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("people.fid1", pairs.get(0)[1].getQualifiedName());
+    assertEquals("employee.id2", pairs.get(1)[0].getQualifiedName());
+    assertEquals("people.fid2", pairs.get(1)[1].getQualifiedName());
+
+    // after exchange of outer and inner
+    pairs = PlannerUtil.getJoinKeyPairs(compositedJoinQual, innerSchema,  outerSchema, false);
+    assertEquals(2, pairs.size());
+    assertEquals("people.fid1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("employee.id1", pairs.get(0)[1].getQualifiedName());
+    assertEquals("people.fid2", pairs.get(1)[0].getQualifiedName());
+    assertEquals("employee.id2", pairs.get(1)[1].getQualifiedName());
+
+    // Theta join (f1 <= f2)
+    EvalNode thetaJoinQual = new BinaryEval(EvalType.LEQ, f1, f2);
+    pairs = PlannerUtil.getJoinKeyPairs(thetaJoinQual, outerSchema,  innerSchema, true);
+    assertEquals(1, pairs.size());
+    assertEquals("employee.id1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("people.fid1", pairs.get(0)[1].getQualifiedName());
+
+    // Composite Theta join (f1 <= f2 AND f3 = f4)
+    EvalNode compositeThetaJoin = new BinaryEval(EvalType.AND, thetaJoinQual, joinQual2);
+    pairs = PlannerUtil.getJoinKeyPairs(compositeThetaJoin, outerSchema,  innerSchema, true);
+    assertEquals(2, pairs.size());
+    assertEquals("employee.id1", pairs.get(0)[0].getQualifiedName());
+    assertEquals("people.fid1", pairs.get(0)[1].getQualifiedName());
+    assertEquals("employee.id2", pairs.get(1)[0].getQualifiedName());
+    assertEquals("people.fid2", pairs.get(1)[1].getQualifiedName());
+  }
+
+  @Test
+  public final void testGetSortKeysFromJoinQual() {
+    Schema outerSchema = new Schema();
+    outerSchema.addColumn("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    outerSchema.addColumn("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    Schema innerSchema = new Schema();
+    innerSchema.addColumn("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    innerSchema.addColumn("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    FieldEval f1 = new FieldEval("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f2 = new FieldEval("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f3 = new FieldEval("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f4 = new FieldEval("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    EvalNode joinQual = new BinaryEval(EvalType.EQUAL, f1, f2);
+    SortSpec[][] sortSpecs = PlannerUtil.getSortKeysFromJoinQual(joinQual, outerSchema, innerSchema);
+    assertEquals(2, sortSpecs.length);
+    assertEquals(1, sortSpecs[0].length);
+    assertEquals(1, sortSpecs[1].length);
+    assertEquals(outerSchema.getColumn("id1"), sortSpecs[0][0].getSortKey());
+    assertEquals(innerSchema.getColumn("fid1"), sortSpecs[1][0].getSortKey());
+
+    // tests for composited join key
+    EvalNode joinQual2 = new BinaryEval(EvalType.EQUAL, f3, f4);
+    EvalNode compositedJoinQual = new BinaryEval(EvalType.AND, joinQual, joinQual2);
+
+    sortSpecs = PlannerUtil.getSortKeysFromJoinQual(compositedJoinQual, outerSchema, innerSchema);
+    assertEquals(2, sortSpecs.length);
+    assertEquals(2, sortSpecs[0].length);
+    assertEquals(2, sortSpecs[1].length);
+    assertEquals(outerSchema.getColumn("id1"), sortSpecs[0][0].getSortKey());
+    assertEquals(outerSchema.getColumn("id2"), sortSpecs[0][1].getSortKey());
+    assertEquals(innerSchema.getColumn("fid1"), sortSpecs[1][0].getSortKey());
+    assertEquals(innerSchema.getColumn("fid2"), sortSpecs[1][1].getSortKey());
+  }
+
+  @Test
+  public final void testComparatorsFromJoinQual() {
+    Schema outerSchema = new Schema();
+    outerSchema.addColumn("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    outerSchema.addColumn("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    Schema innerSchema = new Schema();
+    innerSchema.addColumn("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    innerSchema.addColumn("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    FieldEval f1 = new FieldEval("employee.id1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f2 = new FieldEval("people.fid1", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f3 = new FieldEval("employee.id2", CatalogUtil.newSimpleDataType(Type.INT4));
+    FieldEval f4 = new FieldEval("people.fid2", CatalogUtil.newSimpleDataType(Type.INT4));
+
+    EvalNode joinQual = new BinaryEval(EvalType.EQUAL, f1, f2);
+    TupleComparator[] comparators = PhysicalPlanUtil.getComparatorsFromJoinQual(joinQual, outerSchema, innerSchema);
+
+    Tuple t1 = new VTuple(2);
+    t1.put(0, DatumFactory.createInt4(1));
+    t1.put(1, DatumFactory.createInt4(2));
+
+    Tuple t2 = new VTuple(2);
+    t2.put(0, DatumFactory.createInt4(2));
+    t2.put(1, DatumFactory.createInt4(3));
+
+    TupleComparator outerComparator = comparators[0];
+    assertTrue(outerComparator.compare(t1, t2) < 0);
+    assertTrue(outerComparator.compare(t2, t1) > 0);
+
+    TupleComparator innerComparator = comparators[1];
+    assertTrue(innerComparator.compare(t1, t2) < 0);
+    assertTrue(innerComparator.compare(t2, t1) > 0);
+
+    // tests for composited join key
+    EvalNode joinQual2 = new BinaryEval(EvalType.EQUAL, f3, f4);
+    EvalNode compositedJoinQual = new BinaryEval(EvalType.AND, joinQual, joinQual2);
+    comparators = PhysicalPlanUtil.getComparatorsFromJoinQual(compositedJoinQual, outerSchema, innerSchema);
+
+    outerComparator = comparators[0];
+    assertTrue(outerComparator.compare(t1, t2) < 0);
+    assertTrue(outerComparator.compare(t2, t1) > 0);
+
+    innerComparator = comparators[1];
+    assertTrue(innerComparator.compare(t1, t2) < 0);
+    assertTrue(innerComparator.compare(t2, t1) > 0);
+  }
+
+  @Test
+  public void testGetNonZeroLengthDataFiles() throws Exception {
+    String queryFiles = ClassLoader.getSystemResource("queries").toString() + "/TestSelectQuery";
+    Path path = new Path(queryFiles);
+
+    TableDesc tableDesc = new TableDesc();
+    tableDesc.setName("Test");
+    tableDesc.setUri(path.toUri());
+
+    FileSystem fs = path.getFileSystem(util.getConfiguration());
+
+    List<Path> expectedFiles = new ArrayList<Path>();
+    RemoteIterator<LocatedFileStatus> files = fs.listFiles(path, true);
+    while (files.hasNext()) {
+      LocatedFileStatus file = files.next();
+      if (file.isFile() && file.getLen() > 0) {
+        expectedFiles.add(file.getPath());
+      }
+    }
+    int fileNum = expectedFiles.size() / 5;
+
+    int numResultFiles = 0;
+    for (int i = 0; i <= 5; i++) {
+      int start = i * fileNum;
+
+      FragmentProto[] fragments =
+          PhysicalPlanUtil.getNonZeroLengthDataFiles(util.getConfiguration(), tableDesc, start, fileNum);
+      assertNotNull(fragments);
+
+      numResultFiles += fragments.length;
+      int expectedSize = fileNum;
+      if (i == 5) {
+        //last
+        expectedSize = expectedFiles.size() - (fileNum * 5);
+      }
+
+      comparePath(expectedFiles, fragments, start, expectedSize);
+    }
+
+    assertEquals(expectedFiles.size(), numResultFiles);
+  }
+
+  private void comparePath(List<Path> expectedFiles, FragmentProto[] fragments,
+                           int startIndex, int expectedSize) throws Exception {
+    assertEquals(expectedSize, fragments.length);
+
+    int index = 0;
+
+    for (int i = startIndex; i < startIndex + expectedSize; i++, index++) {
+      FileFragment fragment = FragmentConvertor.convert(util.getConfiguration(), fragments[index]);
+      assertEquals(expectedFiles.get(i), fragment.getPath());
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/a4106883/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestQueryValidation.java
----------------------------------------------------------------------
diff --git a/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestQueryValidation.java b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestQueryValidation.java
new file mode 100644
index 0000000..f2e8e64
--- /dev/null
+++ b/tajo-core-tests/src/test/java/org/apache/tajo/engine/planner/TestQueryValidation.java
@@ -0,0 +1,65 @@
+/*
+ * Lisensed 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.QueryTestCaseBase;
+import org.junit.Test;
+
+import java.io.IOException;
+
+public class TestQueryValidation extends QueryTestCaseBase {
+
+  @Test
+  public void testInsertWithWrongTargetColumn() throws Exception {
+    executeString("CREATE TABLE T1 (col1 int, col2 int)").close();
+    assertInvalidSQL("INSERT INTO T1 (col1, col3) select l_orderkey, l_partkey from default.lineitem");
+  }
+
+  @Test
+  public void testLimitClauses() throws IOException {
+    // select * from lineitem limit 3;
+    assertValidSQLFromFile("valid_limit_1.sql");
+
+    // select * from lineitem limit l_orderkey;
+    assertInvalidSQLFromFile("invalid_limit_1.sql");
+  }
+
+  @Test
+  public void testGroupByClauses() throws IOException {
+    // select l_orderkey from lineitem group by l_orderkey;
+    assertValidSQLFromFile("valid_groupby_1.sql");
+
+    // select * from lineitem group by l_orderkey;
+    assertPlanError("error_groupby_1.sql");
+    // select l_orderkey from lineitem group by l_paerkey;
+    assertPlanError("error_groupby_2.sql");
+  }
+
+  @Test
+  public void testCaseWhenExprs() throws IOException {
+    // See TAJO-1098
+    assertInvalidSQLFromFile("invalid_casewhen_1.sql");
+  }
+
+  @Test
+  public void testUnsupportedStoreType() throws IOException {
+    // See TAJO-1249
+    assertInvalidSQLFromFile("invalid_store_format.sql");
+  }
+}