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 2015/05/16 14:15:36 UTC

[05/10] tajo git commit: TAJO-1542 Refactoring of HashJoinExecs. (contributed by navis, committed by hyunsik)

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterHashJoinExec.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterHashJoinExec.java b/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterHashJoinExec.java
index e20686b..9afc51f 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterHashJoinExec.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterHashJoinExec.java
@@ -319,24 +319,17 @@ public class TestLeftOuterHashJoinExec {
     PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
 
     ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof NLLeftOuterJoinExec) {
-       //for this small data set this is not likely to happen
-      
-      assertEquals(1, 0);
-    }
-    else{
-       Tuple tuple;
-       int count = 0;
-       int i = 1;
-       exec.init();
-  
-       while ((tuple = exec.next()) != null) {
-         //TODO check contents
-         count = count + 1;
-       }
-       exec.close();
-       assertEquals(5, count);
+    Tuple tuple;
+    int count = 0;
+    int i = 1;
+    exec.init();
+
+    while ((tuple = exec.next()) != null) {
+      //TODO check contents
+      count = count + 1;
     }
+    exec.close();
+    assertEquals(5, count);
   }
 
     @Test
@@ -361,24 +354,17 @@ public class TestLeftOuterHashJoinExec {
     PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
 
     ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof NLLeftOuterJoinExec) {
-      //for this small data set this is not likely to happen
-      
-      assertEquals(1, 0);
-    }
-    else{
-       Tuple tuple;
-       int count = 0;
-       int i = 1;
-       exec.init();
-  
-       while ((tuple = exec.next()) != null) {
-         //TODO check contents
-         count = count + 1;
-       }
-       exec.close();
-       assertEquals(7, count);
+    Tuple tuple;
+    int count = 0;
+    int i = 1;
+    exec.init();
+
+    while ((tuple = exec.next()) != null) {
+      //TODO check contents
+      count = count + 1;
     }
+    exec.close();
+    assertEquals(7, count);
   }
 
 
@@ -403,24 +389,17 @@ public class TestLeftOuterHashJoinExec {
     PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
 
     ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof NLLeftOuterJoinExec) {
-      //for this small data set this is not likely to happen
-      
-      assertEquals(1, 0);
-    }
-    else{
-       Tuple tuple;
-       int count = 0;
-       int i = 1;
-       exec.init();
-  
-       while ((tuple = exec.next()) != null) {
-         //TODO check contents
-         count = count + 1;
-       }
-       exec.close();
-       assertEquals(7, count);
+    Tuple tuple;
+    int count = 0;
+    int i = 1;
+    exec.init();
+
+    while ((tuple = exec.next()) != null) {
+      //TODO check contents
+      count = count + 1;
     }
+    exec.close();
+    assertEquals(7, count);
   }
 
   
@@ -445,22 +424,15 @@ public class TestLeftOuterHashJoinExec {
     PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
 
     ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof NLLeftOuterJoinExec) {
-      //for this small data set this is not likely to happen
-      
-      assertEquals(1, 0);
-    }
-    else{
-       int count = 0;
-       exec.init();
-  
-       while (exec.next() != null) {
-         //TODO check contents
-         count = count + 1;
-       }
-       exec.close();
-       assertEquals(0, count);
+    int count = 0;
+    exec.init();
+
+    while (exec.next() != null) {
+      //TODO check contents
+      count = count + 1;
     }
+    exec.close();
+    assertEquals(0, count);
   }
   
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterNLJoinExec.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterNLJoinExec.java b/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterNLJoinExec.java
deleted file mode 100644
index 10fd3d4..0000000
--- a/tajo-core/src/test/java/org/apache/tajo/engine/planner/physical/TestLeftOuterNLJoinExec.java
+++ /dev/null
@@ -1,474 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tajo.engine.planner.physical;
-
-import org.apache.hadoop.fs.Path;
-import org.apache.tajo.LocalTajoTestingUtility;
-import org.apache.tajo.TajoTestingCluster;
-import org.apache.tajo.algebra.Expr;
-import org.apache.tajo.catalog.*;
-import org.apache.tajo.catalog.proto.CatalogProtos.StoreType;
-import org.apache.tajo.common.TajoDataTypes.Type;
-import org.apache.tajo.conf.TajoConf;
-import org.apache.tajo.datum.Datum;
-import org.apache.tajo.datum.DatumFactory;
-import org.apache.tajo.engine.parser.SQLAnalyzer;
-import org.apache.tajo.plan.LogicalPlanner;
-import org.apache.tajo.engine.planner.PhysicalPlanner;
-import org.apache.tajo.engine.planner.PhysicalPlannerImpl;
-import org.apache.tajo.plan.PlanningException;
-import org.apache.tajo.engine.planner.enforce.Enforcer;
-import org.apache.tajo.plan.logical.LogicalNode;
-import org.apache.tajo.engine.query.QueryContext;
-import org.apache.tajo.storage.*;
-import org.apache.tajo.storage.fragment.FileFragment;
-import org.apache.tajo.util.CommonTestingUtil;
-import org.apache.tajo.util.TUtil;
-import org.apache.tajo.worker.TaskAttemptContext;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-
-import static org.apache.tajo.TajoConstants.DEFAULT_DATABASE_NAME;
-import static org.apache.tajo.TajoConstants.DEFAULT_TABLESPACE_NAME;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-public class TestLeftOuterNLJoinExec {
-  private TajoConf conf;
-  private final String TEST_PATH = TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuterNLJoinExec";
-  private TajoTestingCluster util;
-  private CatalogService catalog;
-  private SQLAnalyzer analyzer;
-  private LogicalPlanner planner;
-  private QueryContext defaultContext;
-  private Path testDir;
-
-  private TableDesc dep3;
-  private TableDesc job3;
-  private TableDesc emp3;
-  private TableDesc phone3;
-
-  private final String DEP3_NAME = CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "dep3");
-  private final String JOB3_NAME = CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "job3");
-  private final String EMP3_NAME = CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "emp3");
-  private final String PHONE3_NAME = CatalogUtil.buildFQName(DEFAULT_DATABASE_NAME, "phone3");
-
-  @Before
-  public void setUp() throws Exception {
-    util = new TajoTestingCluster();
-    catalog = util.startCatalogCluster().getCatalog();
-    testDir = CommonTestingUtil.getTestDir(TEST_PATH);
-    catalog.createTablespace(DEFAULT_TABLESPACE_NAME, testDir.toUri().toString());
-    catalog.createDatabase(DEFAULT_DATABASE_NAME, DEFAULT_TABLESPACE_NAME);
-    conf = util.getConfiguration();
-
-    //----------------- dep3 ------------------------------
-    // dep_id | dep_name  | loc_id
-    //--------------------------------
-    //  0     | dep_0     | 1000
-    //  1     | dep_1     | 1001
-    //  2     | dep_2     | 1002
-    //  3     | dep_3     | 1003
-    //  4     | dep_4     | 1004
-    //  5     | dep_5     | 1005
-    //  6     | dep_6     | 1006
-    //  7     | dep_7     | 1007
-    //  8     | dep_8     | 1008
-    //  9     | dep_9     | 1009
-    Schema dep3Schema = new Schema();
-    dep3Schema.addColumn("dep_id", Type.INT4);
-    dep3Schema.addColumn("dep_name", Type.TEXT);
-    dep3Schema.addColumn("loc_id", Type.INT4);
-
-
-    TableMeta dep3Meta = CatalogUtil.newTableMeta("CSV");
-    Path dep3Path = new Path(testDir, "dep3.csv");
-    Appender appender1 = ((FileStorageManager)StorageManager.getFileStorageManager(conf))
-        .getAppender(dep3Meta, dep3Schema, dep3Path);
-    appender1.init();
-    Tuple tuple = new VTuple(dep3Schema.size());
-    for (int i = 0; i < 10; i++) {
-      tuple.put(new Datum[] { DatumFactory.createInt4(i),
-                    DatumFactory.createText("dept_" + i),
-                    DatumFactory.createInt4(1000 + i) });
-      appender1.addTuple(tuple);
-    }
-
-    appender1.flush();
-    appender1.close();
-    dep3 = CatalogUtil.newTableDesc(DEP3_NAME, dep3Schema, dep3Meta, dep3Path);
-    catalog.createTable(dep3);
-
-    //----------------- job3 ------------------------------
-    //  job_id  | job_title
-    // ----------------------
-    //   101    |  job_101
-    //   102    |  job_102
-    //   103    |  job_103
-
-    Schema job3Schema = new Schema();
-    job3Schema.addColumn("job_id", Type.INT4);
-    job3Schema.addColumn("job_title", Type.TEXT);
-
-
-    TableMeta job3Meta = CatalogUtil.newTableMeta("CSV");
-    Path job3Path = new Path(testDir, "job3.csv");
-    Appender appender2 = ((FileStorageManager)StorageManager.getFileStorageManager(conf))
-        .getAppender(job3Meta, job3Schema, job3Path);
-    appender2.init();
-    Tuple tuple2 = new VTuple(job3Schema.size());
-    for (int i = 1; i < 4; i++) {
-      int x = 100 + i;
-      tuple2.put(new Datum[] { DatumFactory.createInt4(100 + i),
-                    DatumFactory.createText("job_" + x) });
-      appender2.addTuple(tuple2);
-    }
-
-    appender2.flush();
-    appender2.close();
-    job3 = CatalogUtil.newTableDesc(JOB3_NAME, job3Schema, job3Meta, job3Path);
-    catalog.createTable(job3);
-
-
-
-    //---------------------emp3 --------------------
-    // emp_id  | first_name | last_name | dep_id | salary | job_id
-    // ------------------------------------------------------------
-    //  11     |  fn_11     |  ln_11    |  1     | 123    | 101
-    //  13     |  fn_13     |  ln_13    |  3     | 369    | 103
-    //  15     |  fn_15     |  ln_15    |  5     | 615    | null
-    //  17     |  fn_17     |  ln_17    |  7     | 861    | null
-    //  19     |  fn_19     |  ln_19    |  9     | 1107   | null
-    //  21     |  fn_21     |  ln_21    |  1     | 123    | 101
-    //  23     |  fn_23     |  ln_23    |  3     | 369    | 103
-
-    Schema emp3Schema = new Schema();
-    emp3Schema.addColumn("emp_id", Type.INT4);
-    emp3Schema.addColumn("first_name", Type.TEXT);
-    emp3Schema.addColumn("last_name", Type.TEXT);
-    emp3Schema.addColumn("dep_id", Type.INT4);
-    emp3Schema.addColumn("salary", Type.FLOAT4);
-    emp3Schema.addColumn("job_id", Type.INT4);
-
-
-    TableMeta emp3Meta = CatalogUtil.newTableMeta("CSV");
-    Path emp3Path = new Path(testDir, "emp3.csv");
-    Appender appender3 = ((FileStorageManager)StorageManager.getFileStorageManager(conf))
-        .getAppender(emp3Meta, emp3Schema, emp3Path);
-    appender3.init();
-    Tuple tuple3 = new VTuple(emp3Schema.size());
-
-    for (int i = 1; i < 4; i += 2) {
-      int x = 10 + i;
-      tuple3.put(new Datum[] { DatumFactory.createInt4(10 + i),
-          DatumFactory.createText("firstname_" + x),
-          DatumFactory.createText("lastname_" + x),
-          DatumFactory.createInt4(i),
-          DatumFactory.createFloat4(123 * i),
-          DatumFactory.createInt4(100 + i) });
-      appender3.addTuple(tuple3);
-
-      int y = 20 + i;
-      tuple3.put(new Datum[] { DatumFactory.createInt4(20 + i),
-          DatumFactory.createText("firstname_" + y),
-          DatumFactory.createText("lastname_" + y),
-          DatumFactory.createInt4(i),
-          DatumFactory.createFloat4(123 * i),
-          DatumFactory.createInt4(100 + i) });
-      appender3.addTuple(tuple3);
-    }
-
-    for (int i = 5; i < 10; i += 2) {
-      int x = 10 + i;
-      tuple3.put(new Datum[] { DatumFactory.createInt4(10 + i),
-          DatumFactory.createText("firstname_" + x),
-          DatumFactory.createText("lastname_" + x),
-          DatumFactory.createInt4(i),
-          DatumFactory.createFloat4(123 * i),
-          DatumFactory.createNullDatum() });
-      appender3.addTuple(tuple3);
-    }
-
-    appender3.flush();
-    appender3.close();
-    emp3 = CatalogUtil.newTableDesc(EMP3_NAME, emp3Schema, emp3Meta, emp3Path);
-    catalog.createTable(emp3);
-
-    // ---------------------phone3 --------------------
-    // emp_id  | phone_number
-    // -----------------------------------------------
-    // this table is empty, no rows
-
-    Schema phone3Schema = new Schema();
-    phone3Schema.addColumn("emp_id", Type.INT4);
-    phone3Schema.addColumn("phone_number", Type.TEXT);
-
-
-    TableMeta phone3Meta = CatalogUtil.newTableMeta("CSV");
-    Path phone3Path = new Path(testDir, "phone3.csv");
-    Appender appender5 = ((FileStorageManager)StorageManager.getFileStorageManager(conf))
-        .getAppender(phone3Meta, phone3Schema, phone3Path);
-    appender5.init();
-    
-    appender5.flush();
-    appender5.close();
-    phone3 = CatalogUtil.newTableDesc(PHONE3_NAME, phone3Schema, phone3Meta, phone3Path);
-    catalog.createTable(phone3);
-
-    analyzer = new SQLAnalyzer();
-    planner = new LogicalPlanner(catalog);
-
-    defaultContext = LocalTajoTestingUtility.createDummyContext(conf);
-  }
-
-  @After
-  public void tearDown() throws Exception {
-    util.shutdownCatalogCluster();
-  }
-  
-  String[] QUERIES = {
-      "select dep3.dep_id, dep_name, emp_id, salary from dep3 left outer join emp3 on dep3.dep_id = emp3.dep_id", //0 no nulls
-      "select job3.job_id, job_title, emp_id, salary from job3 left outer join emp3 on job3.job_id=emp3.job_id", //1 nulls on the right operand
-      "select job3.job_id, job_title, emp_id, salary from emp3 left outer join job3 on job3.job_id=emp3.job_id", //2 nulls on the left side
-      "select emp3.emp_id, first_name, phone_number from emp3 left outer join phone3 on emp3.emp_id = phone3.emp_id", //3 one operand is empty
-      "select phone_number, emp3.emp_id, first_name from phone3 left outer join emp3 on emp3.emp_id = phone3.emp_id" //4 one operand is empty
-  };
-
-  @Test
-  public final void testLeftOuterNLJoinExec0() throws IOException, PlanningException {
-    FileFragment[] dep3Frags = FileStorageManager.splitNG(conf, DEP3_NAME, dep3.getMeta(), new Path(dep3.getPath()),
-        Integer.MAX_VALUE);
-    FileFragment[] emp3Frags = FileStorageManager.splitNG(conf, EMP3_NAME, emp3.getMeta(), new Path(emp3.getPath()),
-        Integer.MAX_VALUE);
-
-    FileFragment[] merged = TUtil.concat(dep3Frags, emp3Frags);
-
-    Path workDir = CommonTestingUtil.getTestDir(TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuterNLJoinExec0");
-    TaskAttemptContext ctx = new TaskAttemptContext(new QueryContext(conf),
-        LocalTajoTestingUtility.newTaskAttemptId(), merged, workDir);
-    ctx.setEnforcer(new Enforcer());
-    Expr context =  analyzer.parse(QUERIES[0]);
-    LogicalNode plan = planner.createPlan(defaultContext, context).getRootBlock().getRoot();
-
-
-    PhysicalPlanner phyPlanner = new PhysicalPlannerImpl(conf);
-    PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
-
-    //maybe plan results with hash join exec algorithm usage. Must convert from HashLeftOuterJoinExec into NLLeftOuterJoinExec
-    ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof HashLeftOuterJoinExec) {
-      HashLeftOuterJoinExec join = proj.getChild();
-      NLLeftOuterJoinExec aJoin = new NLLeftOuterJoinExec(ctx, join.getPlan(), join.getLeftChild(), join.getRightChild());
-      proj.setChild(aJoin);
-      exec = proj;
-    }
-
-    int count = 0;
-    exec.init();
-    while (exec.next() != null) {
-       //TODO check contents
-         count = count + 1;
-    }
-    assertNull(exec.next());
-    exec.close();
-    assertEquals(12, count);
-  }
-
-
-  @Test
-  public final void testLeftOuterNLJoinExec1() throws IOException, PlanningException {
-    FileFragment[] job3Frags = FileStorageManager.splitNG(conf, JOB3_NAME, job3.getMeta(), new Path(job3.getPath()),
-        Integer.MAX_VALUE);
-    FileFragment[] emp3Frags = FileStorageManager.splitNG(conf, EMP3_NAME, emp3.getMeta(), new Path(emp3.getPath()),
-        Integer.MAX_VALUE);
-
-    FileFragment[] merged = TUtil.concat(job3Frags, emp3Frags);
-
-
-    Path workDir = CommonTestingUtil.getTestDir(TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuter_NLJoinExec1");
-    TaskAttemptContext ctx = new TaskAttemptContext(new QueryContext(conf),
-        LocalTajoTestingUtility.newTaskAttemptId(), merged, workDir);
-    ctx.setEnforcer(new Enforcer());
-    Expr context =  analyzer.parse(QUERIES[1]);
-    LogicalNode plan = planner.createPlan(defaultContext, context).getRootBlock().getRoot();
-
-
-    PhysicalPlanner phyPlanner = new PhysicalPlannerImpl(conf);
-    PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
-    
-    //maybe plan results with hash join exec algorithm usage. Must convert from HashLeftOuterJoinExec into NLLeftOuterJoinExec
-    ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof HashLeftOuterJoinExec) {
-      HashLeftOuterJoinExec join = proj.getChild();
-      NLLeftOuterJoinExec aJoin = new NLLeftOuterJoinExec(ctx, join.getPlan(), join.getLeftChild(), join.getRightChild());
-      proj.setChild(aJoin);
-      exec = proj;
-     
-    }
-
-
-    Tuple tuple;
-    int i = 1;
-    int count = 0;
-    exec.init();
-    while ((tuple = exec.next()) != null) {
-       //TODO check contents
-         count = count + 1;
-      
-    }
-    exec.close();
-    assertEquals(5, count);
-  }
-
-  @Test
-  public final void testLeftOuter_NLJoinExec2() throws IOException, PlanningException {
-    FileFragment[] emp3Frags = FileStorageManager.splitNG(conf, EMP3_NAME, emp3.getMeta(), new Path(emp3.getPath()),
-        Integer.MAX_VALUE);
-    FileFragment[] job3Frags = FileStorageManager.splitNG(conf, JOB3_NAME, job3.getMeta(), new Path(job3.getPath()),
-        Integer.MAX_VALUE);
-
-    FileFragment[] merged = TUtil.concat(emp3Frags, job3Frags);
-
-    Path workDir = CommonTestingUtil.getTestDir(TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuter_NLJoinExec2");
-    TaskAttemptContext ctx = new TaskAttemptContext(new QueryContext(conf),
-        LocalTajoTestingUtility.newTaskAttemptId(), merged, workDir);
-    ctx.setEnforcer(new Enforcer());
-    Expr context =  analyzer.parse(QUERIES[2]);
-    LogicalNode plan = planner.createPlan(defaultContext, context).getRootBlock().getRoot();
-
-
-    PhysicalPlanner phyPlanner = new PhysicalPlannerImpl(conf);
-    PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
-    
-    //maybe plan results with hash join exec algorithm usage. Must convert from HashLeftOuterJoinExec into NLLeftOuterJoinExec
-    ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof HashLeftOuterJoinExec) {
-      HashLeftOuterJoinExec join = proj.getChild();
-      NLLeftOuterJoinExec aJoin = new NLLeftOuterJoinExec(ctx, join.getPlan(), join.getLeftChild(), join.getRightChild());
-      proj.setChild(aJoin);
-      exec = proj;
-     
-    }
-
-
-    Tuple tuple;
-    int i = 1;
-    int count = 0;
-    exec.init();
-    while ((tuple = exec.next()) != null) {
-       //TODO check contents
-         count = count + 1;
-      
-    }
-    exec.close();
-    assertEquals(7, count);
-  }
-
-
-  @Test
-  public final void testLeftOuter_NLJoinExec3() throws IOException, PlanningException {
-    FileFragment[] emp3Frags = FileStorageManager.splitNG(conf, EMP3_NAME, emp3.getMeta(), new Path(emp3.getPath()),
-        Integer.MAX_VALUE);
-    FileFragment[] phone3Frags = FileStorageManager.splitNG(conf, PHONE3_NAME, phone3.getMeta(), new Path(phone3.getPath()),
-        Integer.MAX_VALUE);
-
-    FileFragment[] merged = TUtil.concat(emp3Frags, phone3Frags);
-
-    Path workDir = CommonTestingUtil.getTestDir(TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuter_NLJoinExec3");
-    TaskAttemptContext ctx = new TaskAttemptContext(new QueryContext(conf),
-        LocalTajoTestingUtility.newTaskAttemptId(), merged, workDir);
-    ctx.setEnforcer(new Enforcer());
-    Expr context =  analyzer.parse(QUERIES[3]);
-    LogicalNode plan = planner.createPlan(defaultContext, context).getRootBlock().getRoot();
-
-
-    PhysicalPlanner phyPlanner = new PhysicalPlannerImpl(conf);
-    PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
-    
-    //maybe plan results with hash join exec algorithm usage. Must convert from HashLeftOuterJoinExec into NLLeftOuterJoinExec
-    ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof HashLeftOuterJoinExec) {
-      HashLeftOuterJoinExec join = proj.getChild();
-      NLLeftOuterJoinExec aJoin = new NLLeftOuterJoinExec(ctx, join.getPlan(), join.getLeftChild(), join.getRightChild());
-      proj.setChild(aJoin);
-      exec = proj;
-     
-    }
-
-
-    Tuple tuple;
-    int i = 1;
-    int count = 0;
-    exec.init();
-    while ((tuple = exec.next()) != null) {
-       //TODO check contents
-         count = count + 1;
-      
-    }
-    exec.close();
-    assertEquals(7, count);
-  }
-
-    @Test
-  public final void testLeftOuter_NLJoinExec4() throws IOException, PlanningException {
-    FileFragment[] emp3Frags = FileStorageManager.splitNG(conf, EMP3_NAME, emp3.getMeta(), new Path(emp3.getPath()),
-        Integer.MAX_VALUE);
-    FileFragment[] phone3Frags = FileStorageManager.splitNG(conf, PHONE3_NAME, phone3.getMeta(), new Path(phone3.getPath()),
-        Integer.MAX_VALUE);
-
-    FileFragment[] merged = TUtil.concat(phone3Frags, emp3Frags);
-
-    Path workDir = CommonTestingUtil.getTestDir(TajoTestingCluster.DEFAULT_TEST_DIRECTORY + "/TestLeftOuter_NLJoinExec4");
-    TaskAttemptContext ctx = new TaskAttemptContext(new QueryContext(conf),
-        LocalTajoTestingUtility.newTaskAttemptId(), merged, workDir);
-    ctx.setEnforcer(new Enforcer());
-    Expr context =  analyzer.parse(QUERIES[4]);
-    LogicalNode plan = planner.createPlan(defaultContext, context).getRootBlock().getRoot();
-
-
-    PhysicalPlanner phyPlanner = new PhysicalPlannerImpl(conf);
-    PhysicalExec exec = phyPlanner.createPlan(ctx, plan);
-    
-    //maybe plan results with hash join exec algorithm usage. Must convert from HashLeftOuterJoinExec into NLLeftOuterJoinExec
-    ProjectionExec proj = (ProjectionExec) exec;
-    if (proj.getChild() instanceof HashLeftOuterJoinExec) {
-      HashLeftOuterJoinExec join = proj.getChild();
-      NLLeftOuterJoinExec aJoin = new NLLeftOuterJoinExec(ctx, join.getPlan(), join.getLeftChild(), join.getRightChild());
-      proj.setChild(aJoin);
-      exec = proj;
-     
-    }
-
-
-    Tuple tuple;
-    int i = 1;
-    int count = 0;
-    exec.init();
-    while ((tuple = exec.next()) != null) {
-       //TODO check contents
-         count = count + 1;
-      
-    }
-    exec.close();
-    assertEquals(0, count);
-  }
-}

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinFilterOfRowPreservedTable1.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinFilterOfRowPreservedTable1.sql b/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinFilterOfRowPreservedTable1.sql
index 66274d7..50ea371 100644
--- a/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinFilterOfRowPreservedTable1.sql
+++ b/tajo-core/src/test/resources/queries/TestJoinQuery/testJoinFilterOfRowPreservedTable1.sql
@@ -5,4 +5,4 @@ select
   n_regionkey
 from
   region left outer join nation on n_regionkey = r_regionkey and r_name in ('AMERICA', 'ASIA')
-order by r_name;
\ No newline at end of file
+order by r_name,n_name;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-core/src/test/resources/results/TestJoinQuery/testJoinFilterOfRowPreservedTable1.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestJoinQuery/testJoinFilterOfRowPreservedTable1.result b/tajo-core/src/test/resources/results/TestJoinQuery/testJoinFilterOfRowPreservedTable1.result
index 82d5562..d489e3e 100644
--- a/tajo-core/src/test/resources/results/TestJoinQuery/testJoinFilterOfRowPreservedTable1.result
+++ b/tajo-core/src/test/resources/results/TestJoinQuery/testJoinFilterOfRowPreservedTable1.result
@@ -6,10 +6,10 @@ AMERICA,1,BRAZIL,1
 AMERICA,1,CANADA,1
 AMERICA,1,PERU,1
 AMERICA,1,UNITED STATES,1
+ASIA,2,CHINA,2
 ASIA,2,INDIA,2
 ASIA,2,INDONESIA,2
 ASIA,2,JAPAN,2
-ASIA,2,CHINA,2
 ASIA,2,VIETNAM,2
 EUROPE,3,null,null
 MIDDLE EAST,4,null,null
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AggregationFunctionCallEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AggregationFunctionCallEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AggregationFunctionCallEval.java
index cfcc829..33d6565 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AggregationFunctionCallEval.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AggregationFunctionCallEval.java
@@ -78,7 +78,7 @@ public class AggregationFunctionCallEval extends FunctionEval implements Cloneab
   }
 
   public void merge(FunctionContext context, Tuple tuple) {
-    if (!isBinded) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before merge()");
     }
     mergeParam(context, evalParams(tuple));
@@ -99,7 +99,7 @@ public class AggregationFunctionCallEval extends FunctionEval implements Cloneab
   }
 
   public Datum terminate(FunctionContext context) {
-    if (!isBinded) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before terminate()");
     }
     if (!finalPhase) {

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AlgebraicUtil.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AlgebraicUtil.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AlgebraicUtil.java
index 6cf7272..c6b7354 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AlgebraicUtil.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/AlgebraicUtil.java
@@ -21,6 +21,7 @@ package org.apache.tajo.plan.expr;
 import org.apache.tajo.catalog.Column;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 import java.util.Stack;
@@ -327,6 +328,10 @@ public class AlgebraicUtil {
         (expr.getType() == EvalType.LIKE && !((LikePredicateEval)expr).isLeadingWildCard());
   }
 
+  public static EvalNode createSingletonExprFromCNF(Collection<EvalNode> cnfExprs) {
+    return createSingletonExprFromCNF(cnfExprs.toArray(new EvalNode[cnfExprs.size()]));
+  }
+
   /**
    * Convert a list of conjunctive normal forms into a singleton expression.
    *

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNode.java
index 9abb0bc..b154532 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNode.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNode.java
@@ -35,43 +35,44 @@ import org.apache.tajo.storage.Tuple;
  * It is also used for evaluation.
  */
 public abstract class EvalNode implements Cloneable, GsonObject, ProtoObject<PlanProto.EvalNodeTree> {
-	@Expose protected EvalType type;
-  protected boolean isBinded = false;
+  @Expose
+  protected EvalType type;
+  protected transient boolean isBound;
 
   public EvalNode() {
   }
 
-	public EvalNode(EvalType type) {
-		this.type = type;
-	}
-	
-	public EvalType getType() {
-		return this.type;
-	}
-	
-	public abstract DataType getValueType();
+  public EvalNode(EvalType type) {
+    this.type = type;
+  }
+
+  public EvalType getType() {
+    return this.type;
+  }
+
+  public abstract DataType getValueType();
 
   public abstract int childNum();
 
   public abstract EvalNode getChild(int idx);
-	
-	public abstract String getName();
+
+  public abstract String getName();
 
   @Override
-	public String toJson() {
+  public String toJson() {
     return PlanGsonHelper.toJson(this, EvalNode.class);
-	}
+  }
 
   public EvalNode bind(@Nullable EvalContext evalContext, Schema schema) {
     for (int i = 0; i < childNum(); i++) {
       getChild(i).bind(evalContext, schema);
     }
-    isBinded = true;
+    isBound = true;
     return this;
   }
 
-	public <T extends Datum> T eval(Tuple tuple) {
-    if (!isBinded) {
+  public <T extends Datum> T eval(Tuple tuple) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before eval()");
     }
     return null;
@@ -87,7 +88,7 @@ public abstract class EvalNode implements Cloneable, GsonObject, ProtoObject<Pla
   public Object clone() throws CloneNotSupportedException {
     EvalNode evalNode = (EvalNode) super.clone();
     evalNode.type = type;
-    evalNode.isBinded = isBinded;
+    evalNode.isBound = isBound;
     return evalNode;
   }
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/InEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/InEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/InEval.java
index c968bda..7052663 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/InEval.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/InEval.java
@@ -58,7 +58,7 @@ public class InEval extends BinaryEval {
 
   @Override
   public Datum eval(Tuple tuple) {
-    if (!isBinded) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before eval()");
     }
     if (values == null) {

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/PatternMatchPredicateEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/PatternMatchPredicateEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/PatternMatchPredicateEval.java
index cdd8dfb..ec143f7 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/PatternMatchPredicateEval.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/PatternMatchPredicateEval.java
@@ -82,7 +82,7 @@ public abstract class PatternMatchPredicateEval extends BinaryEval {
 
   @Override
   public Datum eval(Tuple tuple) {
-    if (!isBinded) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before eval()");
     }
     Datum predicand = leftExpr.eval(tuple);

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-plan/src/main/java/org/apache/tajo/plan/expr/WindowFunctionEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/WindowFunctionEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/WindowFunctionEval.java
index a39d303..e5b88f2 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/WindowFunctionEval.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/WindowFunctionEval.java
@@ -64,7 +64,7 @@ public class WindowFunctionEval extends AggregationFunctionCallEval implements C
 
   @Override
   public Datum terminate(FunctionContext context) {
-    if (!isBinded) {
+    if (!isBound) {
       throw new IllegalStateException("bind() must be called before terminate()");
     }
     return functionInvoke.terminate(context);

http://git-wip-us.apache.org/repos/asf/tajo/blob/36a703c5/tajo-storage/tajo-storage-common/src/main/java/org/apache/tajo/storage/FrameTuple.java
----------------------------------------------------------------------
diff --git a/tajo-storage/tajo-storage-common/src/main/java/org/apache/tajo/storage/FrameTuple.java b/tajo-storage/tajo-storage-common/src/main/java/org/apache/tajo/storage/FrameTuple.java
index 8b7e2e0..a5561ed 100644
--- a/tajo-storage/tajo-storage-common/src/main/java/org/apache/tajo/storage/FrameTuple.java
+++ b/tajo-storage/tajo-storage-common/src/main/java/org/apache/tajo/storage/FrameTuple.java
@@ -30,7 +30,7 @@ import org.apache.tajo.exception.UnsupportedException;
 /**
  * An instance of FrameTuple is an immutable tuple.
  * It contains two tuples and pretends to be one instance of Tuple for
- * join qual evaluatations.
+ * join qual evaluations.
  */
 public class FrameTuple implements Tuple, Cloneable {
   private int size;
@@ -52,6 +52,18 @@ public class FrameTuple implements Tuple, Cloneable {
     this.right = right;
   }
 
+  public FrameTuple setLeft(Tuple left) {
+    this.left = left;
+    this.leftSize = left.size();
+    return this;
+  }
+
+  public FrameTuple setRight(Tuple right) {
+    this.right = right;
+    this.size = leftSize + right.size();
+    return this;
+  }
+
   @Override
   public int size() {
     return size;