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/08/14 05:33:59 UTC

[1/2] tajo git commit: TAJO-680: Improve the IN operator to support sub queries.

Repository: tajo
Updated Branches:
  refs/heads/master f0ab0ca20 -> 042c3e882


http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/ValueSetEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/ValueSetEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/ValueSetEval.java
new file mode 100644
index 0000000..4e24f5d
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/ValueSetEval.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.expr;
+
+import org.apache.tajo.datum.Datum;
+
+/**
+ * ValueSetEval is an abstract class to represent both {@link RowConstantEval} and {@link SubqueryEval}.
+ * This is allowed only for the right child of {@link InEval}.
+ */
+public abstract class ValueSetEval extends EvalNode implements Cloneable {
+
+  public ValueSetEval(EvalType evalType) {
+    super(evalType);
+  }
+
+  public abstract Datum[] getValues();
+
+  @Override
+  public int childNum() {
+    return 0;
+  }
+
+  @Override
+  public EvalNode getChild(int idx) {
+    return null;
+  }
+
+  @Override
+  public void preOrder(EvalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(EvalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
index 403d0b8..7984024 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/GreedyHeuristicJoinOrderAlgorithm.java
@@ -306,7 +306,7 @@ public class GreedyHeuristicJoinOrderAlgorithm implements JoinOrderAlgorithm {
           if (interchangeableWithRightVertex.contains(rightTarget)) {
             JoinEdge targetEdge = joinGraph.getEdge(leftTarget, rightTarget);
             if (targetEdge == null) {
-              if (joinGraph.isSymmetricJoinOnly()) {
+              if (joinGraph.allowArbitraryCrossJoin()) {
                 // Since the targets of the both sides are searched with symmetric characteristics,
                 // the join type is assumed as CROSS.
                 // TODO: This must be improved to consider a case when a query involves multiple commutative and
@@ -380,6 +380,11 @@ public class GreedyHeuristicJoinOrderAlgorithm implements JoinOrderAlgorithm {
                   SchemaUtil.estimateRowByteSizeWithSchema(joinEdge.getSchema()) /
                   SchemaUtil.estimateRowByteSizeWithSchema(joinEdge.getRightVertex().getSchema()));
           break;
+        case LEFT_ANTI:
+        case LEFT_SEMI:
+          factor *= DEFAULT_SELECTION_FACTOR * SchemaUtil.estimateRowByteSizeWithSchema(joinEdge.getSchema()) /
+              SchemaUtil.estimateRowByteSizeWithSchema(joinEdge.getLeftVertex().getSchema());
+          break;
         case INNER:
         default:
           // by default, do the same operation with that of inner join

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
index 8fcdce7..1d4d48d 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinGraph.java
@@ -18,6 +18,7 @@
 
 package org.apache.tajo.plan.joinorder;
 
+import org.apache.tajo.algebra.JoinType;
 import org.apache.tajo.plan.logical.JoinSpec;
 import org.apache.tajo.plan.util.PlannerUtil;
 import org.apache.tajo.util.graph.SimpleUndirectedGraph;
@@ -29,11 +30,14 @@ import java.util.List;
  */
 public class JoinGraph extends SimpleUndirectedGraph<JoinVertex, JoinEdge> {
 
-  private boolean isSymmetricJoinOnly = true;
+  private boolean allowArbitraryCrossJoin = true;
 
   public JoinEdge addJoin(JoinGraphContext context, JoinSpec joinSpec, JoinVertex left, JoinVertex right) {
     JoinEdge edge = context.getCachedOrNewJoinEdge(joinSpec, left, right);
-    isSymmetricJoinOnly &= PlannerUtil.isCommutativeJoinType(edge.getJoinType());
+    // TODO: the below will be improved after TAJO-1683
+    allowArbitraryCrossJoin &= PlannerUtil.isCommutativeJoinType(edge.getJoinType())
+        || edge.getJoinType() == JoinType.LEFT_SEMI || edge.getJoinType() == JoinType.LEFT_ANTI;
+
     this.addEdge(left, right, edge);
     List<JoinEdge> incomeToLeft = getIncomingEdges(left);
     if (incomeToLeft == null || incomeToLeft.isEmpty()) {
@@ -46,7 +50,7 @@ public class JoinGraph extends SimpleUndirectedGraph<JoinVertex, JoinEdge> {
     return edge;
   }
 
-  public boolean isSymmetricJoinOnly() {
-    return isSymmetricJoinOnly;
+  public boolean allowArbitraryCrossJoin() {
+    return allowArbitraryCrossJoin;
   }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderingUtil.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderingUtil.java b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderingUtil.java
index 3f6a1ca..35a7933 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderingUtil.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/joinorder/JoinOrderingUtil.java
@@ -99,8 +99,10 @@ public class JoinOrderingUtil {
       JoinedRelationsVertex tempLeftChild = new JoinedRelationsVertex(leftEdge);
       JoinEdge tempEdge = context.getCachedOrNewJoinEdge(rightEdge.getJoinSpec(), tempLeftChild,
           rightEdge.getRightVertex());
-      if ((rightEdge.getJoinType() != JoinType.INNER && rightEdge.getJoinType() != JoinType.CROSS)
-          || (leftEdge.getJoinType() != JoinType.INNER && leftEdge.getJoinType() != JoinType.CROSS)) {
+      if ((rightEdge.getJoinType() != JoinType.INNER && rightEdge.getJoinType() != JoinType.CROSS
+          && rightEdge.getJoinType() != JoinType.LEFT_SEMI && rightEdge.getJoinType() != JoinType.LEFT_ANTI)
+          || (leftEdge.getJoinType() != JoinType.INNER && leftEdge.getJoinType() != JoinType.CROSS
+          && leftEdge.getJoinType() != JoinType.LEFT_SEMI && leftEdge.getJoinType() != JoinType.LEFT_ANTI)) {
         if (!findJoinConditionForJoinVertex(context.getCandidateJoinConditions(), tempEdge, true).isEmpty()) {
           return false;
         }
@@ -139,6 +141,8 @@ public class JoinOrderingUtil {
    * (A full B) full C    | A full (B full C)     | Equivalent
    * ==============================================================
    *
+   * Cross, Semi and Anti joins follow the rule of the Inner join.
+   *
    * @param leftType
    * @param rightType
    * @return true if two join types are associative.
@@ -148,8 +152,12 @@ public class JoinOrderingUtil {
       return true;
     }
 
-    if (leftType == JoinType.INNER && rightType == JoinType.CROSS ||
-        leftType == JoinType.CROSS && rightType == JoinType.INNER) {
+    boolean isLeftInner = leftType == JoinType.INNER || leftType == JoinType.CROSS
+        || leftType == JoinType.LEFT_SEMI || leftType == JoinType.LEFT_ANTI;
+    boolean isRightInner = rightType == JoinType.INNER || rightType == JoinType.CROSS
+        || rightType == JoinType.LEFT_SEMI || rightType == JoinType.LEFT_ANTI;
+
+    if (isLeftInner && isRightInner) {
       return true;
     }
 
@@ -164,7 +172,7 @@ public class JoinOrderingUtil {
       return false;
     }
 
-    if ((leftType == JoinType.INNER) || leftType == JoinType.CROSS) {
+    if (isLeftInner) {
       if (rightType == JoinType.LEFT_OUTER) {
         return true;
       } else {

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java
index ced9a36..60a9405 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java
@@ -32,6 +32,8 @@ import org.apache.tajo.catalog.Schema;
  */
 public abstract class RelationNode extends LogicalNode {
 
+  protected boolean nameResolveBase = true;
+
   protected RelationNode(int pid, NodeType nodeType) {
     super(pid, nodeType);
     assert(nodeType == NodeType.SCAN || nodeType == NodeType.PARTITIONS_SCAN || nodeType == NodeType.TABLE_SUBQUERY);
@@ -58,4 +60,12 @@ public abstract class RelationNode extends LogicalNode {
    * @return A logical schema
    */
   public abstract Schema getLogicalSchema();
+
+  public boolean isNameResolveBase() {
+    return nameResolveBase;
+  }
+
+  public void setNameResolveBase(boolean isCandidate) {
+    this.nameResolveBase = isCandidate;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/nameresolver/NameResolver.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/nameresolver/NameResolver.java b/tajo-plan/src/main/java/org/apache/tajo/plan/nameresolver/NameResolver.java
index 3eb51ba..f5b9c43 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/nameresolver/NameResolver.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/nameresolver/NameResolver.java
@@ -26,11 +26,7 @@ import org.apache.tajo.catalog.CatalogUtil;
 import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.NestedPathUtil;
 import org.apache.tajo.catalog.Schema;
-import org.apache.tajo.exception.AmbiguousTableException;
-import org.apache.tajo.exception.UndefinedColumnException;
-import org.apache.tajo.exception.UndefinedTableException;
-import org.apache.tajo.exception.AmbiguousColumnException;
-import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.exception.*;
 import org.apache.tajo.plan.LogicalPlan;
 import org.apache.tajo.plan.PlanningException;
 import org.apache.tajo.plan.logical.RelationNode;
@@ -150,14 +146,31 @@ public abstract class NameResolver {
    * @throws PlanningException
    */
   static Column resolveFromRelsWithinBlock(LogicalPlan plan, LogicalPlan.QueryBlock block,
-                                                  ColumnReferenceExpr columnRef)
+                                           ColumnReferenceExpr columnRef)
       throws AmbiguousColumnException, AmbiguousTableException, UndefinedColumnException, UndefinedTableException {
-
     String qualifier;
     String canonicalName;
 
     if (columnRef.hasQualifier()) {
-      Pair<String, String> normalized = lookupQualifierAndCanonicalName(block, columnRef);
+      Pair<String, String> normalized;
+      try {
+        normalized = lookupQualifierAndCanonicalName(block, columnRef);
+      } catch (UndefinedColumnException udce) {
+        // is it correlated subquery?
+        // if the search column is not found at the current block, find it at all ancestors of the block.
+        LogicalPlan.QueryBlock current = block;
+        while (!plan.getRootBlock().getName().equals(current.getName())) {
+          LogicalPlan.QueryBlock parentBlock = plan.getParentBlock(current);
+          for (RelationNode relationNode : parentBlock.getRelations()) {
+            if (relationNode.getLogicalSchema().containsByQualifiedName(columnRef.getCanonicalName())) {
+              throw new NotImplementedException("Correlated subquery");
+            }
+          }
+          current = parentBlock;
+        }
+
+        throw udce;
+      }
       qualifier = normalized.getFirst();
       canonicalName = normalized.getSecond();
 
@@ -227,9 +240,11 @@ public abstract class NameResolver {
     List<Column> candidates = TUtil.newList();
 
     for (RelationNode rel : block.getRelations()) {
-      Column found = rel.getLogicalSchema().getColumn(columnName);
-      if (found != null) {
-        candidates.add(found);
+      if (rel.isNameResolveBase()) {
+        Column found = rel.getLogicalSchema().getColumn(columnName);
+        if (found != null) {
+          candidates.add(found);
+        }
       }
     }
 
@@ -363,7 +378,7 @@ public abstract class NameResolver {
 
     // throw exception if no column cannot be founded or two or more than columns are founded
     if (guessedRelations.size() == 0) {
-      throw new UndefinedColumnException(columnRef.getQualifier());
+      throw new UndefinedColumnException(columnRef.getCanonicalName());
     } else if (guessedRelations.size() > 1) {
       throw new AmbiguousColumnException(columnRef.getCanonicalName());
     }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteRuleProvider.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteRuleProvider.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteRuleProvider.java
index f73aa54..120529c 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteRuleProvider.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/BaseLogicalPlanRewriteRuleProvider.java
@@ -40,6 +40,8 @@ public class BaseLogicalPlanRewriteRuleProvider extends LogicalPlanRewriteRulePr
     List<Class<? extends LogicalPlanRewriteRule>> rules = TUtil.newList();
 
     rules.add(CommonConditionReduceRule.class);
+    // In-subquery rewrite phase must be executed before the filter push down phase.
+    rules.add(InSubqueryRewriteRule.class);
 
     if (systemConf.getBoolVar(TajoConf.ConfVars.$TEST_FILTER_PUSHDOWN_ENABLED)) {
       rules.add(FilterPushDownRule.class);

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/InSubqueryRewriteRule.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/InSubqueryRewriteRule.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/InSubqueryRewriteRule.java
new file mode 100644
index 0000000..d0ff8b6
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/InSubqueryRewriteRule.java
@@ -0,0 +1,189 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.rewrite.rules;
+
+import com.google.common.base.Preconditions;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.SchemaUtil;
+import org.apache.tajo.exception.TajoException;
+import org.apache.tajo.plan.LogicalPlan;
+import org.apache.tajo.plan.LogicalPlan.QueryBlock;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.*;
+import org.apache.tajo.plan.logical.*;
+import org.apache.tajo.plan.rewrite.LogicalPlanRewriteRule;
+import org.apache.tajo.plan.rewrite.LogicalPlanRewriteRuleContext;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.visitor.BasicLogicalPlanVisitor;
+import org.apache.tajo.util.TUtil;
+
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * InSubqueryRewriteRule finds all subqueries occurring in the where clause with "IN" keywords,
+ * and replaces them with appropriate join plans.
+ * This rule must be executed before {@link FilterPushDownRule}.
+ *
+ */
+public class InSubqueryRewriteRule implements LogicalPlanRewriteRule {
+
+  private static final String NAME = "InSubqueryRewrite";
+  private final Rewriter rewriter = new Rewriter();
+
+  @Override
+  public String getName() {
+    return NAME;
+  }
+
+  @Override
+  public boolean isEligible(LogicalPlanRewriteRuleContext context) {
+    for (LogicalNode eachNode : PlannerUtil.findAllNodes(context.getPlan().getRootNode(), NodeType.SELECTION)) {
+      SelectionNode selectionNode = (SelectionNode) eachNode;
+      if (!extractInSubquery(selectionNode.getQual()).isEmpty()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  static List<InEval> extractInSubquery(EvalNode qual) {
+    List<InEval> inSubqueries = TUtil.newList();
+    for (EvalNode eachQual : EvalTreeUtil.findEvalsByType(qual, EvalType.IN)) {
+      InEval inEval = (InEval) eachQual;
+      if (inEval.getRightExpr().getType() == EvalType.SUBQUERY) {
+        inSubqueries.add(inEval);
+      }
+    }
+    return inSubqueries;
+  }
+
+  @Override
+  public LogicalPlan rewrite(LogicalPlanRewriteRuleContext context) throws TajoException {
+    LogicalPlan.QueryBlock rootBlock = context.getPlan().getRootBlock();
+    LogicalPlan plan = context.getPlan();
+    rewriter.visit(context.getQueryContext(), plan, rootBlock, rootBlock.getRoot(), new Stack<LogicalNode>());
+    return plan;
+  }
+
+  private static final class Rewriter extends BasicLogicalPlanVisitor<Object, Object> {
+    @Override
+    public Object visitFilter(Object context, LogicalPlan plan, LogicalPlan.QueryBlock block, SelectionNode node,
+                              Stack<LogicalNode> stack) throws TajoException {
+      // Since InSubqueryRewriteRule is executed before FilterPushDownRule,
+      // we can expect that in-subqueries are found at only SelectionNode.
+
+      // Visit every child first.
+      List<InEval> inSubqueries = extractInSubquery(node.getQual());
+      stack.push(node);
+      for (InEval eachIn : inSubqueries) {
+        SubqueryEval subqueryEval = eachIn.getRightExpr();
+        QueryBlock childBlock = plan.getBlock(subqueryEval.getSubQueryNode().getSubQuery());
+        visit(context, plan, childBlock, childBlock.getRoot(), stack);
+      }
+      visit(context, plan, block, node.getChild(), stack);
+      stack.pop();
+
+      LogicalNode baseRelation = node.getChild();
+      for (InEval eachIn : inSubqueries) {
+        // 1. find the base relation for the column of the outer query
+
+        // We assume that the left child of an in-subquery is either a FieldEval or a CastEval.
+        Preconditions.checkArgument(eachIn.getLeftExpr().getType() == EvalType.FIELD ||
+            eachIn.getLeftExpr().getType() == EvalType.CAST);
+        EvalNode leftEval = eachIn.getLeftExpr();
+        SubqueryEval subqueryEval = eachIn.getRightExpr();
+        QueryBlock childBlock = plan.getBlock(subqueryEval.getSubQueryNode().getSubQuery());
+
+        // 2. create join
+        JoinType joinType = eachIn.isNot() ? JoinType.LEFT_ANTI : JoinType.LEFT_SEMI;
+        JoinNode joinNode = new JoinNode(plan.newPID());
+        joinNode.init(joinType, baseRelation, subqueryEval.getSubQueryNode());
+        joinNode.setJoinQual(buildJoinCondition(leftEval, subqueryEval.getSubQueryNode()));
+        ProjectionNode projectionNode = PlannerUtil.findTopNode(subqueryEval.getSubQueryNode(), NodeType.PROJECTION);
+        // Insert an aggregation operator rather than just setting the distinct flag of the ProjectionNode
+        // because the performance of distinct aggregation is poor.
+        insertDistinctOperator(plan, childBlock, projectionNode, projectionNode.getChild());
+
+        Schema inSchema = SchemaUtil.merge(joinNode.getLeftChild().getOutSchema(),
+            joinNode.getRightChild().getOutSchema());
+        joinNode.setInSchema(inSchema);
+        joinNode.setOutSchema(node.getOutSchema());
+
+        List<Target> targets = TUtil.newList(PlannerUtil.schemaToTargets(inSchema));
+        joinNode.setTargets(targets.toArray(new Target[targets.size()]));
+
+        block.addJoinType(joinType);
+        block.registerNode(joinNode);
+        plan.addHistory("IN subquery is rewritten into " + (eachIn.isNot() ? "anti" : "semi") + " join.");
+
+        // 3. set the created join as the base relation
+        baseRelation = joinNode;
+      }
+      
+      // 4. remove in quals
+      EvalNode[] originDnfs = AlgebraicUtil.toDisjunctiveNormalFormArray(node.getQual());
+      List<EvalNode> rewrittenDnfs = TUtil.newList();
+      for (EvalNode eachDnf : originDnfs) {
+        Set<EvalNode> cnfs = TUtil.newHashSet(AlgebraicUtil.toConjunctiveNormalFormArray(eachDnf));
+        cnfs.removeAll(inSubqueries);
+        if (!cnfs.isEmpty()) {
+          rewrittenDnfs.add(AlgebraicUtil.createSingletonExprFromCNF(cnfs));
+        }
+      }
+      if (rewrittenDnfs.size() > 0) {
+        node.setQual(AlgebraicUtil.createSingletonExprFromDNF(rewrittenDnfs.toArray(new EvalNode[rewrittenDnfs.size()])));
+        // The current selection node is expected to be removed at the filter push down phase.
+        node.setChild(baseRelation);
+      } else {
+        PlannerUtil.replaceNode(plan, block.getRoot(), node, baseRelation);
+        block.unregisterNode(node);
+      }
+
+      return null;
+    }
+
+    private void insertDistinctOperator(LogicalPlan plan, LogicalPlan.QueryBlock block,
+                                        ProjectionNode projectionNode, LogicalNode child) throws TajoException {
+      if (projectionNode.getChild().getType() != NodeType.GROUP_BY) {
+        Schema outSchema = projectionNode.getOutSchema();
+        GroupbyNode dupRemoval = plan.createNode(GroupbyNode.class);
+        dupRemoval.setChild(child);
+        dupRemoval.setInSchema(projectionNode.getInSchema());
+        dupRemoval.setTargets(PlannerUtil.schemaToTargets(outSchema));
+        dupRemoval.setGroupingColumns(outSchema.toArray());
+
+        block.registerNode(dupRemoval);
+        block.setAggregationRequire();
+
+        projectionNode.setChild(dupRemoval);
+        projectionNode.setInSchema(dupRemoval.getOutSchema());
+      }
+    }
+
+    private EvalNode buildJoinCondition(EvalNode leftField, TableSubQueryNode subQueryNode) {
+      FieldEval rightField = new FieldEval(subQueryNode.getOutSchema().getColumn(0));
+      return new BinaryEval(EvalType.EQUAL, leftField, rightField);
+    }
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/ProjectionPushDownRule.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/ProjectionPushDownRule.java b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/ProjectionPushDownRule.java
index a7cf85e..5322868 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/ProjectionPushDownRule.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/rewrite/rules/ProjectionPushDownRule.java
@@ -1124,7 +1124,7 @@ public class ProjectionPushDownRule extends
 
   @Override
   public LogicalNode visitTableSubQuery(Context upperContext, LogicalPlan plan, LogicalPlan.QueryBlock block,
-                                   TableSubQueryNode node, Stack<LogicalNode> stack) throws TajoException {
+                                        TableSubQueryNode node, Stack<LogicalNode> stack) throws TajoException {
     Context childContext = new Context(plan, upperContext.requiredSet);
     stack.push(node);
     LogicalNode child = super.visitTableSubQuery(childContext, plan, block, node, stack);

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeDeserializer.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeDeserializer.java b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeDeserializer.java
index 3a1d257..fa952ab 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeDeserializer.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeDeserializer.java
@@ -35,6 +35,7 @@ import org.apache.tajo.datum.*;
 import org.apache.tajo.exception.TajoInternalError;
 import org.apache.tajo.plan.expr.*;
 import org.apache.tajo.plan.function.python.PythonScriptEngine;
+import org.apache.tajo.plan.logical.TableSubQueryNode;
 import org.apache.tajo.plan.logical.WindowSpec;
 import org.apache.tajo.plan.serder.PlanProto.WinFunctionEvalSpec;
 
@@ -105,7 +106,7 @@ public class EvalNodeDeserializer {
 
         switch (type) {
         case IN:
-          current = new InEval(lhs, (RowConstantEval) rhs, binProto.getNegative());
+          current = new InEval(lhs, (ValueSetEval) rhs, binProto.getNegative());
           break;
         case LIKE: {
           PlanProto.PatternMatchEvalSpec patternMatchProto = protoNode.getPatternMatch();
@@ -142,6 +143,12 @@ public class EvalNodeDeserializer {
         }
         current = new RowConstantEval(values);
 
+      } else if (type == EvalType.SUBQUERY) {
+        PlanProto.SubqueryEval subqueryProto = protoNode.getSubquery();
+        TableSubQueryNode subQueryNode = (TableSubQueryNode) LogicalNodeDeserializer.deserialize(context, evalContext,
+            subqueryProto.getSubquery());
+        current = new SubqueryEval(subQueryNode);
+
       } else if (type == EvalType.FIELD) {
         CatalogProtos.ColumnProto columnProto = protoNode.getField();
         current = new FieldEval(new Column(columnProto));

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeSerializer.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeSerializer.java b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeSerializer.java
index a03b637..7de0b05 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeSerializer.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/EvalNodeSerializer.java
@@ -307,6 +307,21 @@ public class EvalNodeSerializer
     return function;
   }
 
+  @Override
+  public EvalNode visitSubquery(EvalTreeProtoBuilderContext context, SubqueryEval subquery, Stack<EvalNode> stack) {
+    super.visitSubquery(context, subquery, stack);
+
+    PlanProto.SubqueryEval.Builder subqueryBuilder = PlanProto.SubqueryEval.newBuilder();
+    subqueryBuilder.setSubquery(LogicalNodeSerializer.serialize(subquery.getSubQueryNode()));
+
+    PlanProto.EvalNode.Builder builder = createEvalBuilder(context, subquery);
+    builder.setSubquery(subqueryBuilder);
+
+    context.treeBuilder.addNodes(builder);
+
+    return subquery;
+  }
+
   private WindowFrame buildWindowFrame(WindowSpec.WindowFrame frame) {
     WindowFrame.Builder windowFrameBuilder = WindowFrame.newBuilder();
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeDeserializer.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeDeserializer.java b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeDeserializer.java
index dad9893..6ba525d 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeDeserializer.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeDeserializer.java
@@ -437,6 +437,7 @@ public class LogicalNodeDeserializer {
     }
     scan.setInSchema(convertSchema(protoNode.getInSchema()));
     scan.setOutSchema(convertSchema(protoNode.getOutSchema()));
+    scan.setNameResolveBase(scanProto.getNameResolveBase());
   }
 
   private static IndexScanNode convertIndexScan(OverridableConf context, EvalContext evalContext,
@@ -481,6 +482,7 @@ public class LogicalNodeDeserializer {
     if (proto.getTargetsCount() > 0) {
       tableSubQuery.setTargets(convertTargets(context, evalContext, proto.getTargetsList()));
     }
+    tableSubQuery.setNameResolveBase(proto.getNameResolveBase());
 
     return tableSubQuery;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeSerializer.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeSerializer.java b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeSerializer.java
index ae74e30..13d6433 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeSerializer.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/serder/LogicalNodeSerializer.java
@@ -443,6 +443,7 @@ public class LogicalNodeSerializer extends BasicLogicalPlanVisitor<LogicalNodeSe
     }
 
     scanBuilder.setBroadcast(scan.isBroadcastTable());
+    scanBuilder.setNameResolveBase(scan.isNameResolveBase());
     return scanBuilder;
   }
 
@@ -505,6 +506,7 @@ public class LogicalNodeSerializer extends BasicLogicalPlanVisitor<LogicalNodeSe
     if (node.hasTargets()) {
       builder.addAllTargets(ProtoUtil.<PlanProto.Target>toProtoObjects(node.getTargets()));
     }
+    builder.setNameResolveBase(node.isNameResolveBase());
 
     PlanProto.LogicalNode.Builder nodeBuilder = createNodeBuilder(context, node);
     nodeBuilder.setTableSubQuery(builder);

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/util/ExprFinder.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/util/ExprFinder.java b/tajo-plan/src/main/java/org/apache/tajo/plan/util/ExprFinder.java
index bc9ec28..461ffd6 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/util/ExprFinder.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/util/ExprFinder.java
@@ -24,17 +24,18 @@ import org.apache.tajo.algebra.OpType;
 import org.apache.tajo.algebra.UnaryOperator;
 import org.apache.tajo.exception.TajoException;
 import org.apache.tajo.exception.TajoInternalError;
-import org.apache.tajo.plan.PlanningException;
 import org.apache.tajo.plan.visitor.SimpleAlgebraVisitor;
+import org.apache.tajo.util.TUtil;
 
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 import java.util.Stack;
 
 public class ExprFinder extends SimpleAlgebraVisitor<ExprFinder.Context, Object> {
 
   static class Context {
-    Set<Expr> set = new HashSet<Expr>();
+    List<Expr> set = TUtil.newList();
     OpType targetType;
 
     Context(OpType type) {
@@ -43,17 +44,18 @@ public class ExprFinder extends SimpleAlgebraVisitor<ExprFinder.Context, Object>
   }
 
   public static <T extends Expr> Set<T> finds(Expr expr, OpType type) {
+    return (Set<T>) new HashSet<Expr>(findsInOrder(expr, type));
+  }
+
+  public static <T extends Expr> List<T> findsInOrder(Expr expr, OpType type) {
     Context context = new Context(type);
     ExprFinder finder = new ExprFinder();
-    Stack<Expr> stack = new Stack<Expr>();
-    stack.push(expr);
     try {
       finder.visit(context, new Stack<Expr>(), expr);
     } catch (TajoException e) {
       throw new TajoInternalError(e);
     }
-    stack.pop();
-    return (Set<T>) context.set;
+    return (List<T>) context.set;
   }
 
   public Object visit(Context ctx, Stack<Expr> stack, Expr expr) throws TajoException {

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/util/PlannerUtil.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/util/PlannerUtil.java b/tajo-plan/src/main/java/org/apache/tajo/plan/util/PlannerUtil.java
index e4bf8bc..99e95be 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/util/PlannerUtil.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/util/PlannerUtil.java
@@ -992,4 +992,21 @@ public class PlannerUtil {
 
     return tableDescTobeCreated;
   }
+
+  /**
+   * Extract all in-subqueries from the given qual.
+   *
+   * @param qual
+   * @return
+   */
+  public static List<Expr> extractInSubquery(Expr qual) {
+    List<Expr> inSubqueries = TUtil.newList();
+    for (Expr eachIn : ExprFinder.findsInOrder(qual, OpType.InPredicate)) {
+      InPredicate inPredicate = (InPredicate) eachIn;
+      if (inPredicate.getInValue().getType() == OpType.SimpleTableSubquery) {
+        inSubqueries.add(eachIn);
+      }
+    }
+    return inSubqueries;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/verifier/LogicalPlanVerifier.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/verifier/LogicalPlanVerifier.java b/tajo-plan/src/main/java/org/apache/tajo/plan/verifier/LogicalPlanVerifier.java
index f249def..57e8e3e 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/verifier/LogicalPlanVerifier.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/verifier/LogicalPlanVerifier.java
@@ -19,12 +19,9 @@
 package org.apache.tajo.plan.verifier;
 
 import com.google.common.base.Preconditions;
-import org.apache.tajo.OverridableConf;
-import org.apache.tajo.catalog.CatalogService;
 import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.Schema;
 import org.apache.tajo.common.TajoDataTypes.Type;
-import org.apache.tajo.conf.TajoConf;
 import org.apache.tajo.error.Errors;
 import org.apache.tajo.exception.TajoException;
 import org.apache.tajo.exception.TajoInternalError;
@@ -40,20 +37,20 @@ import java.util.Stack;
 import static org.apache.tajo.plan.verifier.SyntaxErrorUtil.*;
 
 public class LogicalPlanVerifier extends BasicLogicalPlanVisitor<LogicalPlanVerifier.Context, LogicalNode> {
-  public LogicalPlanVerifier(TajoConf conf, CatalogService catalog) {
+  public LogicalPlanVerifier() {
   }
 
   public static class Context {
     VerificationState state;
 
-    public Context(OverridableConf queryContext, VerificationState state) {
+    public Context(VerificationState state) {
       this.state = state;
     }
   }
 
-  public VerificationState verify(OverridableConf queryContext, VerificationState state, LogicalPlan plan)
+  public VerificationState verify(VerificationState state, LogicalPlan plan)
       throws TajoException {
-    Context context = new Context(queryContext, state);
+    Context context = new Context(state);
     visit(context, plan, plan.getRootBlock());
     return context.state;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/visitor/SimpleAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/visitor/SimpleAlgebraVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/visitor/SimpleAlgebraVisitor.java
index 4854d7f..ab1f74d 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/visitor/SimpleAlgebraVisitor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/visitor/SimpleAlgebraVisitor.java
@@ -105,6 +105,12 @@ public abstract class SimpleAlgebraVisitor<CONTEXT, RESULT> extends BaseAlgebraV
   }
 
   @Override
+  public RESULT visitSimpleTableSubquery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubquery expr)
+      throws TajoException {
+    return super.visitSimpleTableSubquery(ctx, stack, expr);
+  }
+
+  @Override
   public RESULT visitRelationList(CONTEXT ctx, Stack<Expr> stack, RelationList expr) throws TajoException {
     return super.visitRelationList(ctx, stack, expr);
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/proto/Plan.proto
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/proto/Plan.proto b/tajo-plan/src/main/proto/Plan.proto
index 5acbbee..da1e187 100644
--- a/tajo-plan/src/main/proto/Plan.proto
+++ b/tajo-plan/src/main/proto/Plan.proto
@@ -112,6 +112,7 @@ message ScanNode {
   repeated Target targets = 4;
   optional EvalNodeTree qual = 5;
   optional bool broadcast = 6;
+  required bool nameResolveBase = 7;
 }
 
 message PartitionScanSpec {
@@ -190,6 +191,7 @@ message TableSubQueryNode {
   required int32 childSeq = 1;
   required string tableName = 2;
   repeated Target targets = 3;
+  required bool nameResolveBase = 4;
 }
 
 message ProjectionNode {
@@ -396,6 +398,8 @@ enum EvalType {
   ROW_CONSTANT = 32;
   FIELD = 33;
   CONST = 34;
+
+  SUBQUERY = 35;
 }
 
 message EvalNodeTree {
@@ -419,6 +423,7 @@ message EvalNode {
   optional CaseWhenEval casewhen = 13;
   optional IfCondEval ifCond = 14;
   optional PatternMatchEvalSpec patternMatch = 15;
+  optional SubqueryEval subquery = 16;
 }
 
 message UnaryEval {
@@ -438,6 +443,10 @@ message PatternMatchEvalSpec { // requires BinaryEval
   optional bool caseSensitive = 1;
 }
 
+message SubqueryEval {
+  required LogicalNodeTree subquery = 1;
+}
+
 message BetweenEval {
   required int32 predicand = 1;
   required int32 begin = 2;


[2/2] tajo git commit: TAJO-680: Improve the IN operator to support sub queries.

Posted by ji...@apache.org.
TAJO-680: Improve the IN operator to support sub queries.

Closes #620


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

Branch: refs/heads/master
Commit: 042c3e882fbb45fffc6fc2988588282ed085614c
Parents: f0ab0ca
Author: Jihoon Son <ji...@apache.org>
Authored: Fri Aug 14 12:33:02 2015 +0900
Committer: Jihoon Son <ji...@apache.org>
Committed: Fri Aug 14 12:33:46 2015 +0900

----------------------------------------------------------------------
 CHANGES                                         |   2 +
 .../org/apache/tajo/algebra/CommonSubquery.java |  60 ++++++
 .../apache/tajo/algebra/ExistsPredicate.java    |   6 +-
 .../java/org/apache/tajo/algebra/OpType.java    |   2 +-
 .../java/org/apache/tajo/algebra/Relation.java  |   8 +-
 .../org/apache/tajo/algebra/RelationList.java   |   3 +-
 .../tajo/algebra/SimpleTableSubQuery.java       |  16 +-
 .../tajo/algebra/TablePrimarySubQuery.java      |  54 +-----
 .../apache/tajo/engine/parser/SQLAnalyzer.java  |   7 +-
 .../org/apache/tajo/master/GlobalEngine.java    |   8 +-
 .../java/org/apache/tajo/QueryTestCaseBase.java |   2 +-
 .../apache/tajo/engine/eval/ExprTestBase.java   |   4 +-
 .../tajo/engine/planner/TestLogicalPlan.java    |   2 +-
 .../tajo/engine/query/TestInSubquery.java       | 177 +++++++++++++++++
 .../TestInSubquery/testInAndNotInSubQuery.sql   |   3 +
 .../queries/TestInSubquery/testInSubQuery.sql   |   1 +
 .../queries/TestInSubquery/testInSubQuery2.sql  |   2 +
 .../TestInSubquery/testInSubQueryWithJoin.sql   |   2 +
 .../testInSubQueryWithOtherConditions.sql       |   2 +
 .../testInSubQueryWithTableSubQuery.sql         |   2 +
 .../TestInSubquery/testMultipleInSubQuery.sql   |   5 +
 .../testMultipleNotInSubQuery.sql               |   3 +
 .../testNestedInAndNotInSubQuery.sql            |   5 +
 .../TestInSubquery/testNestedInSubQuery.sql     |   4 +
 .../TestInSubquery/testNestedInSubQuery2.sql    |   4 +
 .../TestInSubquery/testNestedNotInSubQuery.sql  |   4 +
 .../TestInSubquery/testNotInSubQuery.sql        |   1 +
 .../testSameKeyNameOfOuterAndInnerQueries.sql   |  22 +++
 .../TestInSubquery/testWithAsteriskAndJoin.sql  |   7 +
 .../testInAndNotInSubQuery.result               |  24 +++
 .../TestInSubquery/testInSubQuery.result        |  27 +++
 .../TestInSubquery/testInSubQuery2.result       |   3 +
 .../testInSubQueryWithJoin.result               |   5 +
 .../testInSubQueryWithOtherConditions.result    |  25 +++
 .../testInSubQueryWithTableSubQuery.result      |   4 +
 .../testMultipleInSubQuery.result               |   5 +
 .../testMultipleNotInSubQuery.result            |  20 ++
 .../testNestedInAndNotInSubQuery.result         |   3 +
 .../TestInSubquery/testNestedInSubQuery.result  |   3 +
 .../TestInSubquery/testNestedInSubQuery2.result |   3 +
 .../testNestedNotInSubQuery.result              |   7 +
 .../TestInSubquery/testNotInSubQuery.result     |  22 +++
 ...testSameKeyNameOfOuterAndInnerQueries.result |   3 +
 .../testWithAsteriskAndJoin.result              |   6 +
 .../org/apache/tajo/plan/ExprAnnotator.java     |  17 +-
 .../org/apache/tajo/plan/LogicalOptimizer.java  | 101 ++++++----
 .../java/org/apache/tajo/plan/LogicalPlan.java  |  15 +-
 .../tajo/plan/LogicalPlanPreprocessor.java      |  38 +++-
 .../org/apache/tajo/plan/LogicalPlanner.java    |  45 ++++-
 .../tajo/plan/algebra/AlgebraVisitor.java       |   2 +-
 .../tajo/plan/algebra/BaseAlgebraVisitor.java   |  11 +-
 .../tajo/plan/expr/BasicEvalNodeVisitor.java    |   9 +
 .../apache/tajo/plan/expr/EvalNodeVisitor2.java |   2 +
 .../org/apache/tajo/plan/expr/EvalType.java     |   4 +-
 .../java/org/apache/tajo/plan/expr/InEval.java  |   9 +-
 .../apache/tajo/plan/expr/RowConstantEval.java  |  34 +---
 .../tajo/plan/expr/SimpleEvalNodeVisitor.java   |   8 +
 .../org/apache/tajo/plan/expr/SubqueryEval.java |  99 ++++++++++
 .../org/apache/tajo/plan/expr/ValueSetEval.java |  54 ++++++
 .../GreedyHeuristicJoinOrderAlgorithm.java      |   7 +-
 .../apache/tajo/plan/joinorder/JoinGraph.java   |  12 +-
 .../tajo/plan/joinorder/JoinOrderingUtil.java   |  18 +-
 .../apache/tajo/plan/logical/RelationNode.java  |  10 +
 .../tajo/plan/nameresolver/NameResolver.java    |  39 ++--
 .../BaseLogicalPlanRewriteRuleProvider.java     |   2 +
 .../rewrite/rules/InSubqueryRewriteRule.java    | 189 +++++++++++++++++++
 .../rewrite/rules/ProjectionPushDownRule.java   |   2 +-
 .../tajo/plan/serder/EvalNodeDeserializer.java  |   9 +-
 .../tajo/plan/serder/EvalNodeSerializer.java    |  15 ++
 .../plan/serder/LogicalNodeDeserializer.java    |   2 +
 .../tajo/plan/serder/LogicalNodeSerializer.java |   2 +
 .../org/apache/tajo/plan/util/ExprFinder.java   |  14 +-
 .../org/apache/tajo/plan/util/PlannerUtil.java  |  17 ++
 .../tajo/plan/verifier/LogicalPlanVerifier.java |  11 +-
 .../tajo/plan/visitor/SimpleAlgebraVisitor.java |   6 +
 tajo-plan/src/main/proto/Plan.proto             |   9 +
 76 files changed, 1168 insertions(+), 222 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 66d9bee..0b705e8 100644
--- a/CHANGES
+++ b/CHANGES
@@ -32,6 +32,8 @@ Release 0.11.0 - unreleased
 
   IMPROVEMENT
 
+    TAJO-680: Improve the IN operator to support sub queries. (jihoon)
+
     TAJO-1751: Reduce the client connection timeout. (jinho)
 
     TAJO-1746: Improve resource usage at first request of DefaultTaskScheduler.

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/CommonSubquery.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CommonSubquery.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CommonSubquery.java
new file mode 100644
index 0000000..ec567f0
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CommonSubquery.java
@@ -0,0 +1,60 @@
+/**
+ * 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.algebra;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public abstract class CommonSubquery extends Relation {
+  @Expose
+  @SerializedName("SubPlan")
+  protected Expr subquery;
+
+  protected CommonSubquery(OpType type, String relationName, Expr subquery) {
+    super(type, relationName);
+    this.subquery = subquery;
+  }
+
+  public Expr getSubQuery() {
+    return subquery;
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(subquery);
+  }
+
+  @Override
+  boolean equalsTo(Expr expr) {
+    CommonSubquery another = (CommonSubquery) expr;
+    return subquery.equals(another.subquery);
+  }
+
+  public String toJson() {
+    return JsonHelper.toJson(this);
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CommonSubquery subQuery = (CommonSubquery) super.clone();
+    subQuery.subquery = (Expr) subquery.clone();
+    return subQuery;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
index fa8b3d4..5ee997d 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
@@ -26,7 +26,7 @@ public class ExistsPredicate extends UnaryOperator {
   @Expose @SerializedName("IsNot")
   private boolean not;
 
-  public ExistsPredicate(SimpleTableSubQuery simpleTableSubQuery, boolean not) {
+  public ExistsPredicate(SimpleTableSubquery simpleTableSubQuery, boolean not) {
     super(OpType.ExistsPredicate);
     this.not = not;
     setChild(simpleTableSubQuery);
@@ -36,8 +36,8 @@ public class ExistsPredicate extends UnaryOperator {
     return this.not;
   }
 
-  public SimpleTableSubQuery getSubQuery() {
-    return (SimpleTableSubQuery) getChild();
+  public SimpleTableSubquery getSubQuery() {
+    return (SimpleTableSubquery) getChild();
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
index 47fea64..f3efde5 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
@@ -38,8 +38,8 @@ public enum OpType {
   Union(SetOperation.class),
   Except(SetOperation.class),
   Intersect(SetOperation.class),
-  SimpleTableSubQuery(SimpleTableSubQuery.class),
   TablePrimaryTableSubQuery(TablePrimarySubQuery.class),
+  SimpleTableSubquery(SimpleTableSubquery.class),
   RelationList(RelationList.class),
   Relation(Relation.class),
   ScalarSubQuery(ScalarSubQuery.class),

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
index 2092b67..6769f8a 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
@@ -25,9 +25,9 @@ import org.apache.tajo.util.TUtil;
 
 public class Relation extends Expr {
   @Expose @SerializedName("TableName")
-  private String tableName;
+  protected String tableName;
   @Expose @SerializedName("TableAlias")
-  private String alias;
+  protected String alias;
 
   protected Relation(OpType type, String relationName) {
     super(type);
@@ -46,6 +46,10 @@ public class Relation extends Expr {
     return tableName;
   }
 
+  public void setTableName(String tableName) {
+    this.tableName = tableName;
+  }
+
   public boolean hasAlias() {
     return alias != null;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
index ad7315b..1ae8ead 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
@@ -37,7 +37,8 @@ public class RelationList extends Expr {
       Preconditions.checkArgument(
           rel.getType() == OpType.Relation ||
           rel.getType() == OpType.Join ||
-          rel.getType() == OpType.TablePrimaryTableSubQuery,
+          rel.getType() == OpType.TablePrimaryTableSubQuery ||
+          rel.getType() == OpType.SimpleTableSubquery,
           "Only Relation, Join, or TablePrimarySubQuery can be given to RelationList, but this expr "
               + " is " + rel.getType());
     }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/SimpleTableSubQuery.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SimpleTableSubQuery.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SimpleTableSubQuery.java
index 2332be1..fd2f777 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SimpleTableSubQuery.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SimpleTableSubQuery.java
@@ -18,19 +18,11 @@
 
 package org.apache.tajo.algebra;
 
-public class SimpleTableSubQuery extends UnaryOperator {
+public class SimpleTableSubquery extends CommonSubquery {
 
-  public SimpleTableSubQuery(Expr subquery) {
-    super(OpType.SimpleTableSubQuery);
-    setChild(subquery);
-  }
-
-  public Expr getSubQuery() {
-    return getChild();
-  }
+  public final static String TEMP_RELATION_NAME = "TempSubqueryName";
 
-  @Override
-  boolean equalsTo(Expr expr) {
-    return true;
+  public SimpleTableSubquery(Expr subquery) {
+    super(OpType.SimpleTableSubquery, TEMP_RELATION_NAME, subquery);
   }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
----------------------------------------------------------------------
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
index 6f08b0d..22f49ca 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
@@ -18,59 +18,9 @@
 
 package org.apache.tajo.algebra;
 
-import com.google.common.base.Objects;
-import com.google.gson.annotations.Expose;
-import com.google.gson.annotations.SerializedName;
-import org.apache.tajo.util.TUtil;
-
-public class TablePrimarySubQuery extends Relation {
-  @Expose @SerializedName("SubPlan")
-  private Expr subquery;
-  @Expose @SerializedName("ColumnNames")
-  private String [] columnNames;
+public class TablePrimarySubQuery extends CommonSubquery {
 
   public TablePrimarySubQuery(String relName, Expr subquery) {
-    super(OpType.TablePrimaryTableSubQuery, relName);
-    this.subquery = subquery;
-  }
-
-  public boolean hasColumnNames() {
-    return this.columnNames != null;
-  }
-
-  public void setColumnNames(String[] aliasList) {
-    this.columnNames = aliasList;
-  }
-
-  public String [] getColumnNames() {
-    return columnNames;
-  }
-
-  public Expr getSubQuery() {
-    return subquery;
-  }
-
-  public int hashCode() {
-    return Objects.hashCode(subquery, Objects.hashCode(columnNames));
-  }
-
-  @Override
-  boolean equalsTo(Expr expr) {
-    TablePrimarySubQuery another = (TablePrimarySubQuery) expr;
-    return subquery.equals(another.subquery) && TUtil.checkEquals(columnNames, another.columnNames);
-  }
-
-  public String toJson() {
-    return JsonHelper.toJson(this);
-  }
-
-  @Override
-  public Object clone() throws CloneNotSupportedException {
-    TablePrimarySubQuery subQuery = (TablePrimarySubQuery) super.clone();
-    subQuery.subquery = (Expr) subquery.clone();
-    if (columnNames != null) {
-      subQuery.columnNames = columnNames.clone();
-    }
-    return subQuery;
+    super(OpType.TablePrimaryTableSubQuery, relName, subquery);
   }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index d89a404..6d00dde 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -46,9 +46,6 @@ import static org.apache.tajo.engine.parser.SQLParser.*;
 
 public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
 
-  public SQLAnalyzer() {
-  }
-
   public Expr parse(String sql) {
     ANTLRInputStream input = new ANTLRInputStream(sql);
     SQLLexer lexer = new SQLLexer(input);
@@ -987,7 +984,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
       }
       return new ValueListExpr(exprs);
     } else {
-      return new SimpleTableSubQuery(visitChildren(ctx.table_subquery()));
+      return new SimpleTableSubquery(visitChildren(ctx.table_subquery()));
     }
   }
 
@@ -1047,7 +1044,7 @@ public class SQLAnalyzer extends SQLParserBaseVisitor<Expr> {
 
   @Override
   public ExistsPredicate visitExists_predicate(SQLParser.Exists_predicateContext ctx) {
-    return new ExistsPredicate(new SimpleTableSubQuery(visitTable_subquery(ctx.table_subquery())), ctx.NOT() != null);
+    return new ExistsPredicate(new SimpleTableSubquery(visitTable_subquery(ctx.table_subquery())), ctx.NOT() != null);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java b/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
index 20780ec..f1f1e3e 100644
--- a/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
+++ b/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
@@ -100,7 +100,7 @@ public class GlobalEngine extends AbstractService {
       planner = new LogicalPlanner(context.getCatalog(), TablespaceManager.getInstance());
       // Access path rewriter is enabled only in QueryMasterTask
       optimizer = new LogicalOptimizer(context.getConf(), context.getCatalog());
-      annotatedPlanVerifier = new LogicalPlanVerifier(context.getConf(), context.getCatalog());
+      annotatedPlanVerifier = new LogicalPlanVerifier();
     } catch (Throwable t) {
       LOG.error(t.getMessage(), t);
       throw new RuntimeException(t);
@@ -283,8 +283,8 @@ public class GlobalEngine extends AbstractService {
     LOG.info("Optimized Query: \n" + plan.toString());
     LOG.info("=============================================");
 
-    annotatedPlanVerifier.verify(queryContext, state, plan);
-    verifyInsertTableSchema(queryContext, state, plan);
+    annotatedPlanVerifier.verify(state, plan);
+    verifyInsertTableSchema(state, plan);
 
     if (!state.verified()) {
       for (Throwable error : state.getErrors()) {
@@ -295,7 +295,7 @@ public class GlobalEngine extends AbstractService {
     return plan;
   }
 
-  private void verifyInsertTableSchema(QueryContext queryContext, VerificationState state, LogicalPlan plan) {
+  private void verifyInsertTableSchema(VerificationState state, LogicalPlan plan) {
     String storeType = PlannerUtil.getStoreType(plan);
     if (storeType != null) {
       LogicalRootNode rootNode = plan.getRootBlock().getRoot();

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java b/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
index bcce612..89e53d5 100644
--- a/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
+++ b/tajo-core/src/test/java/org/apache/tajo/QueryTestCaseBase.java
@@ -295,7 +295,7 @@ public class QueryTestCaseBase {
     }
     LogicalPlan plan = planner.createPlan(context, expr);
     optimizer.optimize(plan);
-    postVerifier.verify(context, state, plan);
+    postVerifier.verify(state, plan);
 
     return state;
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java b/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java
index 6fe1510..f2b6477 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/eval/ExprTestBase.java
@@ -104,7 +104,7 @@ public class ExprTestBase {
     preLogicalPlanVerifier = new PreLogicalPlanVerifier(cat);
     planner = new LogicalPlanner(cat, TablespaceManager.getInstance());
     optimizer = new LogicalOptimizer(util.getConfiguration(), cat);
-    annotatedPlanVerifier = new LogicalPlanVerifier(util.getConfiguration(), cat);
+    annotatedPlanVerifier = new LogicalPlanVerifier();
   }
 
   @AfterClass
@@ -149,7 +149,7 @@ public class ExprTestBase {
     }
     LogicalPlan plan = planner.createPlan(context, expr, true);
     optimizer.optimize(context, plan);
-    annotatedPlanVerifier.verify(context, state, plan);
+    annotatedPlanVerifier.verify(state, plan);
 
     if (state.getErrors().size() > 0) {
       assertFalse(state.getErrors().get(0).getMessage(), true);

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlan.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlan.java b/tajo-core/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlan.java
index dc9e2b0..d49c43e 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlan.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/planner/TestLogicalPlan.java
@@ -49,7 +49,7 @@ public class TestLogicalPlan {
 
   @Test
   public final void testQueryBlockGraph() {
-    LogicalPlan plan = new LogicalPlan(planner);
+    LogicalPlan plan = new LogicalPlan();
     LogicalPlan.QueryBlock root = plan.newAndGetBlock(LogicalPlan.ROOT_BLOCK);
     LogicalPlan.QueryBlock new1 = plan.newQueryBlock();
     LogicalPlan.QueryBlock new2 = plan.newQueryBlock();

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/java/org/apache/tajo/engine/query/TestInSubquery.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestInSubquery.java b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestInSubquery.java
new file mode 100644
index 0000000..fe465f1
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestInSubquery.java
@@ -0,0 +1,177 @@
+/**
+ * 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.query;
+
+import org.apache.tajo.IntegrationTest;
+import org.apache.tajo.NamedTest;
+import org.apache.tajo.error.Errors.ResultCode;
+import org.apache.tajo.exception.TajoRuntimeException;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.SQLException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+@Category(IntegrationTest.class)
+@RunWith(Parameterized.class)
+@NamedTest("TestJoinQuery")
+public class TestInSubquery extends TestJoinQuery {
+
+  public TestInSubquery(String joinOption) throws Exception {
+    super(joinOption);
+  }
+
+  @BeforeClass
+  public static void setup() throws Exception {
+    TestJoinQuery.setup();
+  }
+
+  @AfterClass
+  public static void classTearDown() throws SQLException {
+    TestJoinQuery.classTearDown();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInSubQuery2() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testNestedInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInSubQueryWithOtherConditions() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testMultipleInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInSubQueryWithJoin() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInSubQueryWithTableSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testNotInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testMultipleNotInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testNestedNotInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testInAndNotInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testNestedInAndNotInSubQuery() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testNestedInSubQuery2() throws Exception {
+    // select c_name from customer
+    // where c_nationkey in (
+    //    select n_nationkey from nation where n_name like 'C%' and n_regionkey in (
+    //    select count(*)-1 from region where r_regionkey > 0 and r_regionkey < 3))
+    runSimpleTests();
+  }
+
+  @Test()
+  public final void testCorrelatedSubQuery() throws Exception {
+    // Use try-catch clause to verify the exact error message
+    try {
+      executeString("select * from nation where n_regionkey in (select r_regionkey from region where n_name > r_name)");
+      fail("Correlated subquery must raise the UnimplementedException.");
+    } catch (TajoRuntimeException e) {
+      assertEquals(ResultCode.NOT_IMPLEMENTED, e.getErrorCode());
+    }
+  }
+
+  @Test
+  @Option(withExplain = false, withExplainGlobal = false, parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testSameKeyNameOfOuterAndInnerQueries() throws Exception {
+    runSimpleTests();
+  }
+
+  @Test
+  @Option(parameterized = true, sort = true)
+  @SimpleTest()
+  public final void testWithAsteriskAndJoin() throws Exception {
+    // select * from lineitem, orders where l_orderkey = o_orderkey and l_partkey in
+    // (select l_partkey from lineitem where l_linenumber in (1, 3, 5, 7, 9))
+    runSimpleTests();
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInAndNotInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInAndNotInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInAndNotInSubQuery.sql
new file mode 100644
index 0000000..8ef5077
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInAndNotInSubQuery.sql
@@ -0,0 +1,3 @@
+select n_name from nation
+where n_regionkey in (select r_regionkey from region)
+  and n_nationkey not in (select s_nationkey from supplier)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery.sql
new file mode 100644
index 0000000..48928a1
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery.sql
@@ -0,0 +1 @@
+select n_name from nation where n_regionkey in (select r_regionkey from region)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery2.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery2.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery2.sql
new file mode 100644
index 0000000..eff1e7b
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQuery2.sql
@@ -0,0 +1,2 @@
+select n_name from nation
+where n_nationkey in (select count(*) from region)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithJoin.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithJoin.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithJoin.sql
new file mode 100644
index 0000000..178b601
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithJoin.sql
@@ -0,0 +1,2 @@
+select n_name from nation, supplier
+where n_regionkey in (select r_regionkey from region) and n_nationkey = s_nationkey
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithOtherConditions.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithOtherConditions.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithOtherConditions.sql
new file mode 100644
index 0000000..47a23fa
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithOtherConditions.sql
@@ -0,0 +1,2 @@
+select n_name from nation where n_regionkey in (
+  select r_regionkey from region) and n_nationkey > 1
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithTableSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithTableSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithTableSubQuery.sql
new file mode 100644
index 0000000..8645df1
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testInSubQueryWithTableSubQuery.sql
@@ -0,0 +1,2 @@
+select n_name from (select * from nation where n_nationkey > 1 and n_nationkey < 10) as T
+where n_regionkey in (select r_regionkey from region where r_regionkey > 1 and r_regionkey < 3);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleInSubQuery.sql
new file mode 100644
index 0000000..b93041f
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleInSubQuery.sql
@@ -0,0 +1,5 @@
+select n_name from nation
+where
+  n_regionkey in (select r_regionkey from region)
+  and
+  n_nationkey in (select s_nationkey from supplier)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleNotInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleNotInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleNotInSubQuery.sql
new file mode 100644
index 0000000..bd85ded
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testMultipleNotInSubQuery.sql
@@ -0,0 +1,3 @@
+select n_name from nation
+where n_nationkey not in (select r_regionkey from region)
+  and n_nationkey not in (select s_nationkey from supplier)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInAndNotInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInAndNotInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInAndNotInSubQuery.sql
new file mode 100644
index 0000000..8e4c6ba
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInAndNotInSubQuery.sql
@@ -0,0 +1,5 @@
+select c_name from customer
+  where c_nationkey in (
+    select n_nationkey from nation where n_name like 'C%' and n_regionkey
+    not in (
+      select count(*) from region where r_regionkey > 0 and r_regionkey < 3))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery.sql
new file mode 100644
index 0000000..902dfd7
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery.sql
@@ -0,0 +1,4 @@
+select c_name from customer
+where c_nationkey in (
+  select n_nationkey from nation where n_name like 'C%' and n_regionkey in (
+    select r_regionkey from region where r_regionkey > 0 and r_regionkey < 3))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery2.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery2.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery2.sql
new file mode 100644
index 0000000..4965ec2
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedInSubQuery2.sql
@@ -0,0 +1,4 @@
+select c_name from customer
+  where c_nationkey in (
+    select n_nationkey from nation where n_name like 'C%' and n_regionkey in (
+    select count(*) - 1 from region where r_regionkey > 0 and r_regionkey < 3))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testNestedNotInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testNestedNotInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedNotInSubQuery.sql
new file mode 100644
index 0000000..a2cea67
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testNestedNotInSubQuery.sql
@@ -0,0 +1,4 @@
+select c_name from customer
+where c_nationkey not in (
+  select n_nationkey from nation where n_name like 'C%' and n_regionkey not in (
+    select r_regionkey from region where r_regionkey > 0 and r_regionkey < 3))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testNotInSubQuery.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testNotInSubQuery.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testNotInSubQuery.sql
new file mode 100644
index 0000000..cb05624
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testNotInSubQuery.sql
@@ -0,0 +1 @@
+select n_name from nation where n_nationkey not in (select r_regionkey from region)
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.sql
new file mode 100644
index 0000000..c8d3bf4
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.sql
@@ -0,0 +1,22 @@
+select
+  n_regionkey, count(*)
+from
+  customer, lineitem, orders, supplier, nation
+where
+  l_orderkey = o_orderkey and
+  c_custkey = o_custkey and
+  l_linenumber = s_suppkey and
+  l_partkey in (
+    select
+      l_partkey
+    from
+      lineitem
+    where
+      l_linenumber in (1, 3, 5, 7, 9)
+  ) and
+  n_nationkey = c_nationkey
+group by
+  n_regionkey
+order by
+  n_regionkey
+limit 100;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/queries/TestInSubquery/testWithAsteriskAndJoin.sql
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/queries/TestInSubquery/testWithAsteriskAndJoin.sql b/tajo-core/src/test/resources/queries/TestInSubquery/testWithAsteriskAndJoin.sql
new file mode 100644
index 0000000..d5e2bfa
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestInSubquery/testWithAsteriskAndJoin.sql
@@ -0,0 +1,7 @@
+select
+  *
+from
+  lineitem, orders
+where
+  l_orderkey = o_orderkey and
+  l_partkey in (select l_partkey from lineitem where l_linenumber in (1, 3, 5, 7, 9))
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInAndNotInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInAndNotInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testInAndNotInSubQuery.result
new file mode 100644
index 0000000..1acab75
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInAndNotInSubQuery.result
@@ -0,0 +1,24 @@
+n_name
+-------------------------------
+ALGERIA
+BRAZIL
+CANADA
+CHINA
+EGYPT
+FRANCE
+GERMANY
+INDIA
+INDONESIA
+IRAN
+IRAQ
+JAPAN
+JORDAN
+KENYA
+MOZAMBIQUE
+PERU
+ROMANIA
+RUSSIA
+SAUDI ARABIA
+UNITED KINGDOM
+UNITED STATES
+VIETNAM

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery.result
new file mode 100644
index 0000000..7644296
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery.result
@@ -0,0 +1,27 @@
+n_name
+-------------------------------
+ALGERIA
+ARGENTINA
+BRAZIL
+CANADA
+CHINA
+EGYPT
+ETHIOPIA
+FRANCE
+GERMANY
+INDIA
+INDONESIA
+IRAN
+IRAQ
+JAPAN
+JORDAN
+KENYA
+MOROCCO
+MOZAMBIQUE
+PERU
+ROMANIA
+RUSSIA
+SAUDI ARABIA
+UNITED KINGDOM
+UNITED STATES
+VIETNAM

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery2.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery2.result b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery2.result
new file mode 100644
index 0000000..fbab93d
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQuery2.result
@@ -0,0 +1,3 @@
+n_name
+-------------------------------
+ETHIOPIA

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithJoin.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithJoin.result b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithJoin.result
new file mode 100644
index 0000000..1deee15
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithJoin.result
@@ -0,0 +1,5 @@
+n_name
+-------------------------------
+ARGENTINA
+ETHIOPIA
+MOROCCO

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithOtherConditions.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithOtherConditions.result b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithOtherConditions.result
new file mode 100644
index 0000000..8367869
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithOtherConditions.result
@@ -0,0 +1,25 @@
+n_name
+-------------------------------
+BRAZIL
+CANADA
+CHINA
+EGYPT
+ETHIOPIA
+FRANCE
+GERMANY
+INDIA
+INDONESIA
+IRAN
+IRAQ
+JAPAN
+JORDAN
+KENYA
+MOROCCO
+MOZAMBIQUE
+PERU
+ROMANIA
+RUSSIA
+SAUDI ARABIA
+UNITED KINGDOM
+UNITED STATES
+VIETNAM

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithTableSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithTableSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithTableSubQuery.result
new file mode 100644
index 0000000..d386836
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testInSubQueryWithTableSubQuery.result
@@ -0,0 +1,4 @@
+n_name
+-------------------------------
+INDIA
+INDONESIA

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testMultipleInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testMultipleInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testMultipleInSubQuery.result
new file mode 100644
index 0000000..1deee15
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testMultipleInSubQuery.result
@@ -0,0 +1,5 @@
+n_name
+-------------------------------
+ARGENTINA
+ETHIOPIA
+MOROCCO

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testMultipleNotInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testMultipleNotInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testMultipleNotInSubQuery.result
new file mode 100644
index 0000000..a1e17d5
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testMultipleNotInSubQuery.result
@@ -0,0 +1,20 @@
+n_name
+-------------------------------
+CHINA
+FRANCE
+GERMANY
+INDIA
+INDONESIA
+IRAN
+IRAQ
+JAPAN
+JORDAN
+KENYA
+MOZAMBIQUE
+PERU
+ROMANIA
+RUSSIA
+SAUDI ARABIA
+UNITED KINGDOM
+UNITED STATES
+VIETNAM

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testNestedInAndNotInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testNestedInAndNotInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInAndNotInSubQuery.result
new file mode 100644
index 0000000..51e570a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInAndNotInSubQuery.result
@@ -0,0 +1,3 @@
+c_name
+-------------------------------
+Customer#000000005

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery.result
new file mode 100644
index 0000000..51e570a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery.result
@@ -0,0 +1,3 @@
+c_name
+-------------------------------
+Customer#000000005

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery2.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery2.result b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery2.result
new file mode 100644
index 0000000..51e570a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testNestedInSubQuery2.result
@@ -0,0 +1,3 @@
+c_name
+-------------------------------
+Customer#000000005

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testNestedNotInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testNestedNotInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testNestedNotInSubQuery.result
new file mode 100644
index 0000000..e746b35
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testNestedNotInSubQuery.result
@@ -0,0 +1,7 @@
+c_name
+-------------------------------
+Customer#000000001
+Customer#000000002
+Customer#000000003
+Customer#000000004
+Customer#000000005

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testNotInSubQuery.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testNotInSubQuery.result b/tajo-core/src/test/resources/results/TestInSubquery/testNotInSubQuery.result
new file mode 100644
index 0000000..50b69bd
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testNotInSubQuery.result
@@ -0,0 +1,22 @@
+n_name
+-------------------------------
+CHINA
+ETHIOPIA
+FRANCE
+GERMANY
+INDIA
+INDONESIA
+IRAN
+IRAQ
+JAPAN
+JORDAN
+KENYA
+MOROCCO
+MOZAMBIQUE
+PERU
+ROMANIA
+RUSSIA
+SAUDI ARABIA
+UNITED KINGDOM
+UNITED STATES
+VIETNAM

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.result b/tajo-core/src/test/resources/results/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.result
new file mode 100644
index 0000000..e3d9398
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testSameKeyNameOfOuterAndInnerQueries.result
@@ -0,0 +1,3 @@
+n_regionkey,?count
+-------------------------------
+1,1

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-core/src/test/resources/results/TestInSubquery/testWithAsteriskAndJoin.result
----------------------------------------------------------------------
diff --git a/tajo-core/src/test/resources/results/TestInSubquery/testWithAsteriskAndJoin.result b/tajo-core/src/test/resources/results/TestInSubquery/testWithAsteriskAndJoin.result
new file mode 100644
index 0000000..734ce36
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestInSubquery/testWithAsteriskAndJoin.result
@@ -0,0 +1,6 @@
+l_orderkey,l_partkey,l_suppkey,l_linenumber,l_quantity,l_extendedprice,l_discount,l_tax,l_returnflag,l_linestatus,l_shipdate,l_commitdate,l_receiptdate,l_shipinstruct,l_shipmode,l_comment,o_orderkey,o_custkey,o_orderstatus,o_totalprice,o_orderdate,o_orderpriority,o_clerk,o_shippriority,o_comment
+-------------------------------
+1,1,7311,2,36.0,45983.16,0.09,0.06,N,O,1996-04-12,1996-02-28,1996-04-20,TAKE BACK RETURN,MAIL,ly final dependencies: slyly bold ,1,3,O,173665.47,1996-01-02,5-LOW,Clerk#000000951,0,nstructions sleep furiously among 
+1,1,7706,1,17.0,21168.23,0.04,0.02,N,O,1996-03-13,1996-02-12,1996-03-22,DELIVER IN PERSON,TRUCK,egular courts above the,1,3,O,173665.47,1996-01-02,5-LOW,Clerk#000000951,0,nstructions sleep furiously among 
+2,2,1191,1,38.0,44694.46,0.0,0.05,N,O,1997-01-28,1997-01-14,1997-02-02,TAKE BACK RETURN,RAIL,ven requests. deposits breach a,2,4,O,46929.18,1996-12-01,1-URGENT,Clerk#000000880,0, foxes. pending accounts at the pending, silent asymptot
+3,2,1798,1,45.0,54058.05,0.06,0.0,R,F,1994-02-02,1994-01-04,1994-02-23,NONE,AIR,ongside of the furiously brave acco,3,2,F,193846.25,1993-10-14,5-LOW,Clerk#000000955,0,sly final accounts boost. carefully regular ideas cajole carefully. depos

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/ExprAnnotator.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/ExprAnnotator.java b/tajo-plan/src/main/java/org/apache/tajo/plan/ExprAnnotator.java
index a44b526..b062e8e 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/ExprAnnotator.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/ExprAnnotator.java
@@ -37,6 +37,7 @@ import org.apache.tajo.exception.UnsupportedException;
 import org.apache.tajo.plan.algebra.BaseAlgebraVisitor;
 import org.apache.tajo.plan.expr.*;
 import org.apache.tajo.plan.logical.NodeType;
+import org.apache.tajo.plan.logical.TableSubQueryNode;
 import org.apache.tajo.plan.nameresolver.NameResolver;
 import org.apache.tajo.plan.nameresolver.NameResolvingMode;
 import org.apache.tajo.util.Pair;
@@ -363,12 +364,12 @@ public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, Eva
   public EvalNode visitInPredicate(Context ctx, Stack<Expr> stack, InPredicate expr) throws TajoException {
     stack.push(expr);
     EvalNode lhs = visit(ctx, stack, expr.getLeft());
-    RowConstantEval rowConstantEval = (RowConstantEval) visit(ctx, stack, expr.getInValue());
+    ValueSetEval valueSetEval = (ValueSetEval) visit(ctx, stack, expr.getInValue());
     stack.pop();
 
-    Pair<EvalNode, EvalNode> pair = convertTypesIfNecessary(ctx, lhs, rowConstantEval);
+    Pair<EvalNode, EvalNode> pair = convertTypesIfNecessary(ctx, lhs, valueSetEval);
 
-    return new InEval(pair.getFirst(), (RowConstantEval) pair.getSecond(), expr.isNot());
+    return new InEval(pair.getFirst(), (ValueSetEval) pair.getSecond(), expr.isNot());
   }
 
   @Override
@@ -386,6 +387,16 @@ public class ExprAnnotator extends BaseAlgebraVisitor<ExprAnnotator.Context, Eva
   }
 
   @Override
+  public EvalNode visitSimpleTableSubquery(Context ctx, Stack<Expr> stack, SimpleTableSubquery expr)
+      throws TajoException {
+    if (stack.peek().getType() == OpType.InPredicate) {
+      // In the case of in-subquery, stop visiting because the subquery expr is not expression.
+      return new SubqueryEval((TableSubQueryNode) ctx.currentBlock.getNodeFromExpr(expr));
+    } else {
+      return super.visitSimpleTableSubquery(ctx, stack, expr);
+    }
+  }
+
   public EvalNode visitExistsPredicate(Context ctx, Stack<Expr> stack, ExistsPredicate expr) throws TajoException {
     throw new NotImplementedException("EXISTS clause");
   }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalOptimizer.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalOptimizer.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalOptimizer.java
index b1d8ce5..96617d1 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalOptimizer.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalOptimizer.java
@@ -19,6 +19,7 @@
 package org.apache.tajo.plan;
 
 import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Preconditions;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.classification.InterfaceStability;
@@ -29,6 +30,7 @@ import org.apache.tajo.algebra.JoinType;
 import org.apache.tajo.catalog.CatalogService;
 import org.apache.tajo.conf.TajoConf;
 import org.apache.tajo.conf.TajoConf.ConfVars;
+import org.apache.tajo.plan.expr.*;
 import org.apache.tajo.exception.TajoException;
 import org.apache.tajo.plan.expr.AlgebraicUtil;
 import org.apache.tajo.plan.expr.EvalNode;
@@ -274,51 +276,74 @@ public class LogicalOptimizer {
         throws TajoException {
       super.visitJoin(context, plan, block, joinNode, stack);
 
-      // given a join node, find the relations which are nearest to the join in the query.
-      RelationNode leftChild = findMostRightRelation(plan, block, joinNode.getLeftChild());
-      RelationNode rightChild = findMostLeftRelation(plan, block, joinNode.getRightChild());
-      RelationVertex leftVertex = new RelationVertex(leftChild);
-      RelationVertex rightVertex = new RelationVertex(rightChild);
+      if (joinNode.getJoinType() == JoinType.LEFT_SEMI || joinNode.getJoinType() == JoinType.LEFT_ANTI) {
+        // In case of in-subquery, the left vertex must be the relation of the left column of the in qual.
+        // In addition, the join qual can be evaluated only at the join node for in-subquery,
+        // we don't need to consider moving it to other joins.
+
+        BinaryEval joinQual = (BinaryEval) joinNode.getJoinQual();
+        Preconditions.checkArgument(joinQual.getLeftExpr().getType() == EvalType.FIELD ||
+            joinQual.getLeftExpr().getType() == EvalType.CAST);
+        FieldEval leftColumn = null;
+        if (joinQual.getLeftExpr().getType() == EvalType.FIELD) {
+          leftColumn = joinQual.getLeftExpr();
+        } else if (joinQual.getLeftExpr().getType() == EvalType.CAST) {
+          leftColumn = (FieldEval) ((CastEval)joinQual.getLeftExpr()).getOperand();
+        }
+        RelationNode leftChild = block.getRelation(leftColumn.getQualifier());
+        RelationNode rightChild = joinNode.getRightChild();
+        RelationVertex leftVertex = new RelationVertex(leftChild);
+        RelationVertex rightVertex = new RelationVertex(rightChild);
 
-      JoinEdge edge = context.getJoinGraph().addJoin(context, joinNode.getJoinSpec(), leftVertex, rightVertex);
+        context.getJoinGraph().addJoin(context, joinNode.getJoinSpec(), leftVertex, rightVertex);
+      } else {
 
-      // find all possible predicates for this join edge
-      Set<EvalNode> joinConditions = TUtil.newHashSet();
-      if (joinNode.hasJoinQual()) {
-        Set<EvalNode> originPredicates = joinNode.getJoinSpec().getPredicates();
-        for (EvalNode predicate : joinNode.getJoinSpec().getPredicates()) {
-          if (EvalTreeUtil.isJoinQual(block, leftVertex.getSchema(), rightVertex.getSchema(), predicate, false)) {
-            if (JoinOrderingUtil.checkIfEvaluatedAtEdge(predicate, edge, true)) {
+        // given a join node, find the relations which are nearest to the join in the query.
+        RelationNode leftChild = findMostRightRelation(plan, block, joinNode.getLeftChild());
+        RelationNode rightChild = findMostLeftRelation(plan, block, joinNode.getRightChild());
+        RelationVertex leftVertex = new RelationVertex(leftChild);
+        RelationVertex rightVertex = new RelationVertex(rightChild);
+
+        JoinEdge edge = context.getJoinGraph().addJoin(context, joinNode.getJoinSpec(), leftVertex, rightVertex);
+
+        // find all possible predicates for this join edge
+        Set<EvalNode> joinConditions = TUtil.newHashSet();
+        if (joinNode.hasJoinQual()) {
+          Set<EvalNode> originPredicates = joinNode.getJoinSpec().getPredicates();
+          for (EvalNode predicate : joinNode.getJoinSpec().getPredicates()) {
+            if (EvalTreeUtil.isJoinQual(block, leftVertex.getSchema(), rightVertex.getSchema(), predicate, false)) {
+              if (JoinOrderingUtil.checkIfEvaluatedAtEdge(predicate, edge, true)) {
+                joinConditions.add(predicate);
+              }
+            } else {
               joinConditions.add(predicate);
             }
-          } else {
-            joinConditions.add(predicate);
           }
+          // find predicates which cannot be evaluated at this join
+          originPredicates.removeAll(joinConditions);
+          context.addCandidateJoinConditions(originPredicates);
+          originPredicates.clear();
+          originPredicates.addAll(joinConditions);
         }
-        // find predicates which cannot be evaluated at this join
-        originPredicates.removeAll(joinConditions);
-        context.addCandidateJoinConditions(originPredicates);
-        originPredicates.clear();
-        originPredicates.addAll(joinConditions);
-      }
 
-      joinConditions.addAll(JoinOrderingUtil.findJoinConditionForJoinVertex(context.getCandidateJoinConditions(), edge,
-          true));
-      joinConditions.addAll(JoinOrderingUtil.findJoinConditionForJoinVertex(context.getCandidateJoinFilters(), edge,
-          false));
-      context.markAsEvaluatedJoinConditions(joinConditions);
-      context.markAsEvaluatedJoinFilters(joinConditions);
-      edge.addJoinPredicates(joinConditions);
-      if (edge.getJoinType() == JoinType.INNER && edge.getJoinQual().isEmpty()) {
-        edge.getJoinSpec().setType(JoinType.CROSS);
-      }
-
-      if (PlannerUtil.isCommutativeJoinType(edge.getJoinType())) {
-        JoinEdge commutativeEdge = context.getCachedOrNewJoinEdge(edge.getJoinSpec(), edge.getRightVertex(),
-            edge.getLeftVertex());
-        commutativeEdge.addJoinPredicates(joinConditions);
-        context.getJoinGraph().addEdge(commutativeEdge.getLeftVertex(), commutativeEdge.getRightVertex(),
-            commutativeEdge);
+        joinConditions.addAll(JoinOrderingUtil.findJoinConditionForJoinVertex(context.getCandidateJoinConditions(), edge,
+            true));
+        joinConditions.addAll(JoinOrderingUtil.findJoinConditionForJoinVertex(context.getCandidateJoinFilters(), edge,
+            false));
+        context.markAsEvaluatedJoinConditions(joinConditions);
+        context.markAsEvaluatedJoinFilters(joinConditions);
+        edge.addJoinPredicates(joinConditions);
+        if (edge.getJoinType() == JoinType.INNER && edge.getJoinQual().isEmpty()) {
+          edge.getJoinSpec().setType(JoinType.CROSS);
+        }
+        
+        if (PlannerUtil.isCommutativeJoinType(edge.getJoinType())) {
+          JoinEdge commutativeEdge = context.getCachedOrNewJoinEdge(edge.getJoinSpec(), edge.getRightVertex(),
+              edge.getLeftVertex());
+          commutativeEdge.addJoinPredicates(joinConditions);
+          context.getJoinGraph().addEdge(commutativeEdge.getLeftVertex(), commutativeEdge.getRightVertex(),
+              commutativeEdge);
+        }
       }
 
       return joinNode;

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
index eab939d..b7df810 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlan.java
@@ -57,12 +57,11 @@ public class LogicalPlan {
   /** it indicates the root block */
   public static final String ROOT_BLOCK = VIRTUAL_TABLE_PREFIX + "ROOT";
   public static final String NONAME_BLOCK_PREFIX = VIRTUAL_TABLE_PREFIX + "QB_";
-  public static final String NONAME_SUBQUERY_PREFIX = "?SubQuery_";
+  public static final String NONAME_SUBQUERY_PREFIX = VIRTUAL_TABLE_PREFIX + "SQ_";
   private static final int NO_SEQUENCE_PID = -1;
   private int nextPid = 0;
   private Integer noNameBlockId = 0;
   private Integer noNameColumnId = 0;
-  private Integer noNameSubqueryId = 0;
 
   /** a map from between a block name to a block plan */
   private Map<String, QueryBlock> queryBlocks = new LinkedHashMap<String, QueryBlock>();
@@ -74,16 +73,13 @@ public class LogicalPlan {
   /** planning and optimization log */
   private List<String> planingHistory = Lists.newArrayList();
 
-  private static enum ExplainType {
+  private enum ExplainType {
     NOT_EXPLAIN,
     EXPLAIN_LOGICAL,
     EXPLAIN_GLOBAL
   }
   private ExplainType explainType = ExplainType.NOT_EXPLAIN;
 
-  public LogicalPlan(LogicalPlanner planner) {
-  }
-
   /**
    * Create a LogicalNode instance for a type. Each a LogicalNode instance is given an unique plan node id (PID).
    *
@@ -161,13 +157,6 @@ public class LogicalPlan {
   }
 
   /**
-   * It generates a unique table subquery name
-   */
-  public String generateUniqueSubQueryName() {
-    return NONAME_SUBQUERY_PREFIX + noNameSubqueryId++;
-  }
-
-  /**
    * It generates an unique column name from Expr. It is usually used for an expression or predicate without
    * a specified name (i.e., alias).
    * Here, some expressions require to be identified with their names in the future.

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
index 76907f2..f2c15ac 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanPreprocessor.java
@@ -33,7 +33,6 @@ import org.apache.tajo.plan.logical.*;
 import org.apache.tajo.plan.nameresolver.NameResolver;
 import org.apache.tajo.plan.nameresolver.NameResolvingMode;
 import org.apache.tajo.plan.util.PlannerUtil;
-import org.apache.tajo.catalog.SchemaUtil;
 import org.apache.tajo.plan.visitor.SimpleAlgebraVisitor;
 import org.apache.tajo.util.TUtil;
 
@@ -124,8 +123,10 @@ public class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanner.P
 
       while (iterator.hasNext()) {
         relationOp = iterator.next();
-        schema = relationOp.getLogicalSchema();
-        resolvedColumns.addAll(schema.getRootColumns());
+        if (relationOp.isNameResolveBase()) {
+          schema = relationOp.getLogicalSchema();
+          resolvedColumns.addAll(schema.getRootColumns());
+        }
       }
 
       if (resolvedColumns.size() == 0) {
@@ -346,6 +347,13 @@ public class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanner.P
   public LogicalNode visitFilter(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, Selection expr)
       throws TajoException {
     stack.push(expr);
+    // Since filter push down will be done later, it is guaranteed that in-subqueries are found at only selection.
+    for (Expr eachQual : PlannerUtil.extractInSubquery(expr.getQual())) {
+      InPredicate inPredicate = (InPredicate) eachQual;
+      stack.push(inPredicate);
+      visit(ctx, stack, inPredicate.getRight());
+      stack.pop();
+    }
     LogicalNode child = visit(ctx, stack, expr.getChild());
     stack.pop();
 
@@ -415,6 +423,30 @@ public class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanner.P
     TableSubQueryNode node = ctx.plan.createNode(TableSubQueryNode.class);
     node.init(CatalogUtil.buildFQName(ctx.queryContext.get(SessionVars.CURRENT_DATABASE), expr.getName()), child);
     ctx.queryBlock.addRelation(node);
+
+    return node;
+  }
+
+  @Override
+  public LogicalNode visitSimpleTableSubquery(LogicalPlanner.PlanContext ctx, Stack<Expr> stack, SimpleTableSubquery expr)
+    throws TajoException {
+    LogicalPlanner.PlanContext newContext;
+    // Note: TableSubQuery always has a table name.
+    // SELECT .... FROM (SELECT ...) TB_NAME <-
+    QueryBlock queryBlock = ctx.plan.newQueryBlock();
+    newContext = new LogicalPlanner.PlanContext(ctx, queryBlock);
+    LogicalNode child = super.visitSimpleTableSubquery(newContext, stack, expr);
+    queryBlock.setRoot(child);
+
+    // a table subquery should be dealt as a relation.
+    TableSubQueryNode node = ctx.plan.createNode(TableSubQueryNode.class);
+    node.init(CatalogUtil.buildFQName(ctx.queryContext.get(SessionVars.CURRENT_DATABASE),
+        ctx.generateUniqueSubQueryName()), child);
+    ctx.queryBlock.addRelation(node);
+    if (stack.peek().getType() == OpType.InPredicate) {
+      // In-subquery and scalar subquery cannot be the base for name resolution.
+      node.setNameResolveBase(false);
+    }
     return node;
   }
 

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
index 9b114f1..4b17b0e 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/LogicalPlanner.java
@@ -99,7 +99,9 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     QueryBlock queryBlock;
     EvalTreeOptimizer evalOptimizer;
     TimeZone timeZone;
+    List<Expr> unplannedExprs = TUtil.newList();
     boolean debugOrUnitTests;
+    Integer noNameSubqueryId = 0;
 
     public PlanContext(OverridableConf context, LogicalPlan plan, QueryBlock block, EvalTreeOptimizer evalOptimizer,
                        boolean debugOrUnitTests) {
@@ -137,6 +139,13 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
       return "block=" + queryBlock.getName() + ", relNum=" + queryBlock.getRelations().size() + ", "+
           queryBlock.namedExprsMgr.toString();
     }
+
+    /**
+     * It generates a unique table subquery name
+     */
+    public String generateUniqueSubQueryName() {
+      return LogicalPlan.NONAME_SUBQUERY_PREFIX + noNameSubqueryId++;
+    }
   }
 
   /**
@@ -152,7 +161,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
   @VisibleForTesting
   public LogicalPlan createPlan(OverridableConf queryContext, Expr expr, boolean debug) throws TajoException {
 
-    LogicalPlan plan = new LogicalPlan(this);
+    LogicalPlan plan = new LogicalPlan();
 
     QueryBlock rootBlock = plan.newAndGetBlock(LogicalPlan.ROOT_BLOCK);
     PlanContext context = new PlanContext(queryContext, plan, rootBlock, evalOptimizer, debug);
@@ -234,8 +243,6 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
   public LogicalNode visitProjection(PlanContext context, Stack<Expr> stack, Projection projection)
       throws TajoException {
 
-
-    LogicalPlan plan = context.plan;
     QueryBlock block = context.queryBlock;
 
     // If a non-from statement is given
@@ -279,6 +286,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     projectionNode.init(projection.isDistinct(), targets);
     projectionNode.setChild(child);
     projectionNode.setInSchema(child.getOutSchema());
+    projectionNode.setOutSchema(PlannerUtil.targetToSchema(targets));
 
     if (projection.isDistinct() && block.hasNode(NodeType.GROUP_BY)) {
       throw makeSyntaxError("Cannot support grouping and distinct at the same time yet");
@@ -373,7 +381,7 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
   }
 
   private interface Matcher {
-    public boolean isMatch(Expr expr);
+    boolean isMatch(Expr expr);
   }
 
   public List<Integer> normalize(PlanContext context, Projection projection, ExprNormalizedResult [] normalizedExprList,
@@ -1089,6 +1097,12 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     // Visit and Build Child Plan
     ////////////////////////////////////////////////////////
     stack.push(selection);
+    // Since filter push down will be done later, it is guaranteed that in-subqueries are found at only selection.
+    for (Expr eachQual : PlannerUtil.extractInSubquery(selection.getQual())) {
+      InPredicate inPredicate = (InPredicate) eachQual;
+      visit(context, stack, inPredicate.getInValue());
+      context.unplannedExprs.add(inPredicate.getInValue());
+    }
     LogicalNode child = visit(context, stack, selection.getChild());
     stack.pop();
     ////////////////////////////////////////////////////////
@@ -1384,13 +1398,26 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     }
   }
 
+  @Override
   public TableSubQueryNode visitTableSubQuery(PlanContext context, Stack<Expr> stack, TablePrimarySubQuery expr)
       throws TajoException {
+    return visitCommonTableSubquery(context, stack, expr);
+  }
+
+  @Override
+  public TableSubQueryNode visitSimpleTableSubquery(PlanContext context, Stack<Expr> stack, SimpleTableSubquery expr)
+      throws TajoException {
+    return visitCommonTableSubquery(context, stack, expr);
+  }
+
+  private TableSubQueryNode visitCommonTableSubquery(PlanContext context, Stack<Expr> stack, CommonSubquery expr)
+      throws TajoException {
     QueryBlock currentBlock = context.queryBlock;
     QueryBlock childBlock = context.plan.getBlock(context.plan.getBlockNameByExpr(expr.getSubQuery()));
     context.plan.connectBlocks(childBlock, currentBlock, BlockType.TableSubQuery);
 
     PlanContext newContext = new PlanContext(context, childBlock);
+    context.plan.connectBlocks(childBlock, context.queryBlock, BlockType.TableSubQuery);
     LogicalNode child = visit(newContext, new Stack<Expr>(), expr.getSubQuery());
     TableSubQueryNode subQueryNode = currentBlock.getNodeFromExpr(expr);
 
@@ -1400,7 +1427,8 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     return subQueryNode;
   }
 
-  private void setTargetOfTableSubQuery (PlanContext context, QueryBlock block, TableSubQueryNode subQueryNode) throws TajoException {
+  private void setTargetOfTableSubQuery (PlanContext context, QueryBlock block, TableSubQueryNode subQueryNode)
+      throws TajoException {
     // Add additional expressions required in upper nodes.
     Set<String> newlyEvaluatedExprs = TUtil.newHashSet();
     for (NamedExpr rawTarget : block.namedExprsMgr.getAllNamedExprs()) {
@@ -1457,12 +1485,15 @@ public class LogicalPlanner extends BaseAlgebraVisitor<LogicalPlanner.PlanContex
     return resultingNode;
   }
 
-  private ProjectionNode insertProjectionGroupbyBeforeSetOperation(PlanContext context, SetOperationNode setOperationNode) throws TajoException {
+  private ProjectionNode insertProjectionGroupbyBeforeSetOperation(PlanContext context,
+                                                                   SetOperationNode setOperationNode)
+      throws TajoException {
     QueryBlock currentBlock = context.queryBlock;
 
     // make table subquery node which has set operation as its subquery
     TableSubQueryNode setOpTableSubQueryNode = context.plan.createNode(TableSubQueryNode.class);
-    setOpTableSubQueryNode.init(CatalogUtil.buildFQName(context.queryContext.get(SessionVars.CURRENT_DATABASE), context.plan.generateUniqueSubQueryName()), setOperationNode);
+    setOpTableSubQueryNode.init(CatalogUtil.buildFQName(context.queryContext.get(SessionVars.CURRENT_DATABASE),
+        context.generateUniqueSubQueryName()), setOperationNode);
     setTargetOfTableSubQuery(context, currentBlock, setOpTableSubQueryNode);
     currentBlock.registerNode(setOpTableSubQueryNode);
     currentBlock.addRelation(setOpTableSubQueryNode);

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/AlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/AlgebraVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/AlgebraVisitor.java
index 6149080..c795c09 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/AlgebraVisitor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/AlgebraVisitor.java
@@ -38,7 +38,7 @@ public interface AlgebraVisitor<CONTEXT, RESULT> {
   RESULT visitUnion(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws TajoException;
   RESULT visitExcept(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws TajoException;
   RESULT visitIntersect(CONTEXT ctx, Stack<Expr> stack, SetOperation expr) throws TajoException;
-  RESULT visitSimpleTableSubQuery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubQuery expr) throws TajoException;
+  RESULT visitSimpleTableSubquery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubquery expr) throws TajoException;
   RESULT visitTableSubQuery(CONTEXT ctx, Stack<Expr> stack, TablePrimarySubQuery expr) throws TajoException;
   RESULT visitRelationList(CONTEXT ctx, Stack<Expr> stack, RelationList expr) throws TajoException;
   RESULT visitRelation(CONTEXT ctx, Stack<Expr> stack, Relation expr) throws TajoException;

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/BaseAlgebraVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/BaseAlgebraVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/BaseAlgebraVisitor.java
index 2b4fb30..2d200fc 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/BaseAlgebraVisitor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/algebra/BaseAlgebraVisitor.java
@@ -86,8 +86,8 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
     case Intersect:
       current = visitIntersect(ctx, stack, (SetOperation) expr);
       break;
-    case SimpleTableSubQuery:
-      current = visitSimpleTableSubQuery(ctx, stack, (SimpleTableSubQuery) expr);
+    case SimpleTableSubquery:
+      current = visitSimpleTableSubquery(ctx, stack, (SimpleTableSubquery) expr);
       break;
     case TablePrimaryTableSubQuery:
       current = visitTableSubQuery(ctx, stack, (TablePrimarySubQuery) expr);
@@ -404,9 +404,12 @@ public class BaseAlgebraVisitor<CONTEXT, RESULT> implements AlgebraVisitor<CONTE
   }
 
   @Override
-  public RESULT visitSimpleTableSubQuery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubQuery expr)
+  public RESULT visitSimpleTableSubquery(CONTEXT ctx, Stack<Expr> stack, SimpleTableSubquery expr)
       throws TajoException {
-    return visitDefaultUnaryExpr(ctx, stack, expr);
+    stack.push(expr);
+    RESULT child = visit(ctx, stack, expr.getSubQuery());
+    stack.pop();
+    return child;
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/BasicEvalNodeVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/BasicEvalNodeVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/BasicEvalNodeVisitor.java
index 59be24a..84da79e 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/BasicEvalNodeVisitor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/BasicEvalNodeVisitor.java
@@ -136,6 +136,10 @@ public class BasicEvalNodeVisitor<CONTEXT, RESULT> implements EvalNodeVisitor2<C
         result = visitCast(context, (CastEval) evalNode, stack);
         break;
 
+      case SUBQUERY:
+        result = visitSubquery(context, (SubqueryEval) evalNode, stack);
+        break;
+
       default:
         throw new UnsupportedException("Unknown EvalType: " + evalNode);
     }
@@ -342,4 +346,9 @@ public class BasicEvalNodeVisitor<CONTEXT, RESULT> implements EvalNodeVisitor2<C
   public RESULT visitCast(CONTEXT context, CastEval castEval, Stack<EvalNode> stack) {
     return visitDefaultUnaryEval(context, castEval, stack);
   }
+
+  @Override
+  public RESULT visitSubquery(CONTEXT context, SubqueryEval signedEval, Stack<EvalNode> stack) {
+    return null;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNodeVisitor2.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNodeVisitor2.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNodeVisitor2.java
index 43729ac..ed4e940 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNodeVisitor2.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalNodeVisitor2.java
@@ -69,4 +69,6 @@ public interface EvalNodeVisitor2<CONTEXT, RESULT> {
   RESULT visitSigned(CONTEXT context, SignedEval signedEval, Stack<EvalNode> stack);
 
   RESULT visitCast(CONTEXT context, CastEval signedEval, Stack<EvalNode> stack);
+
+  RESULT visitSubquery(CONTEXT context, SubqueryEval signedEval, Stack<EvalNode> stack);
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalType.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalType.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalType.java
index c1df658..2c2a52f 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalType.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/EvalType.java
@@ -65,7 +65,9 @@ public enum EvalType {
   CAST(CastEval.class),
   ROW_CONSTANT(RowConstantEval.class),
   FIELD(FieldEval.class),
-  CONST(ConstEval.class);
+  CONST(ConstEval.class),
+
+  SUBQUERY(SubqueryEval.class);
 
   private Class<? extends EvalNode> baseClass;
   private String operatorName;

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/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 7052663..70346b4 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
@@ -19,15 +19,14 @@
 package org.apache.tajo.plan.expr;
 
 
-import com.google.common.collect.Sets;
 import com.google.gson.annotations.Expose;
-
 import org.apache.tajo.catalog.CatalogUtil;
 import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.Datum;
 import org.apache.tajo.datum.DatumFactory;
 import org.apache.tajo.datum.NullDatum;
 import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.util.TUtil;
 
 import java.util.Set;
 
@@ -37,7 +36,7 @@ public class InEval extends BinaryEval {
   @Expose private boolean not;
   Set<Datum> values;
 
-  public InEval(EvalNode lhs, RowConstantEval valueList, boolean not) {
+  public InEval(EvalNode lhs, ValueSetEval valueList, boolean not) {
     super(EvalType.IN, lhs, valueList);
     this.not = not;
   }
@@ -62,7 +61,7 @@ public class InEval extends BinaryEval {
       throw new IllegalStateException("bind() must be called before eval()");
     }
     if (values == null) {
-      values = Sets.newHashSet(((RowConstantEval)rightExpr).getValues());
+      values = TUtil.newHashSet(((ValueSetEval) rightExpr).getValues());
     }
 
     Datum leftValue = leftExpr.eval(tuple);
@@ -93,6 +92,6 @@ public class InEval extends BinaryEval {
   }
 
   public String toString() {
-    return leftExpr + " IN (" + rightExpr + ")";
+    return leftExpr + (not? " NOT" : "") + " IN (" + rightExpr + ")";
   }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/RowConstantEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/RowConstantEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/RowConstantEval.java
index eddb022..d2dae5a 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/RowConstantEval.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/RowConstantEval.java
@@ -18,10 +18,7 @@
 
 package org.apache.tajo.plan.expr;
 
-import java.util.Arrays;
-
 import com.google.gson.annotations.Expose;
-
 import org.apache.tajo.catalog.CatalogUtil;
 import org.apache.tajo.datum.Datum;
 import org.apache.tajo.datum.NullDatum;
@@ -29,9 +26,11 @@ import org.apache.tajo.storage.Tuple;
 import org.apache.tajo.util.StringUtils;
 import org.apache.tajo.util.TUtil;
 
+import java.util.Arrays;
+
 import static org.apache.tajo.common.TajoDataTypes.DataType;
 
-public class RowConstantEval extends EvalNode {
+public class RowConstantEval extends ValueSetEval {
   @Expose Datum [] values;
 
   public RowConstantEval(Datum [] values) {
@@ -45,16 +44,6 @@ public class RowConstantEval extends EvalNode {
   }
 
   @Override
-  public int childNum() {
-    return 0;
-  }
-
-  @Override
-  public EvalNode getChild(int idx) {
-    return null;
-  }
-
-  @Override
   public String getName() {
     return "ROW";
   }
@@ -66,10 +55,6 @@ public class RowConstantEval extends EvalNode {
     return NullDatum.get();
   }
 
-  public Datum [] getValues() {
-    return values;
-  }
-
   @Override
   public int hashCode() {
     final int prime = 31;
@@ -92,14 +77,6 @@ public class RowConstantEval extends EvalNode {
     return StringUtils.join(values);
   }
 
-  public void preOrder(EvalNodeVisitor visitor) {
-    visitor.visit(this);
-  }
-
-  public void postOrder(EvalNodeVisitor visitor) {
-    visitor.visit(this);
-  }
-
   @Override
   public Object clone() throws CloneNotSupportedException {
     RowConstantEval rowConstantEval = (RowConstantEval) super.clone();
@@ -109,4 +86,9 @@ public class RowConstantEval extends EvalNode {
     }
     return rowConstantEval;
   }
+
+  @Override
+  public Datum[] getValues() {
+    return values;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SimpleEvalNodeVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SimpleEvalNodeVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SimpleEvalNodeVisitor.java
index 61a25a9..9515fe8 100644
--- a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SimpleEvalNodeVisitor.java
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SimpleEvalNodeVisitor.java
@@ -76,6 +76,10 @@ public abstract class SimpleEvalNodeVisitor<CONTEXT> {
         result = visitFuncCall(context, (FunctionEval) evalNode, stack);
         break;
 
+      case SUBQUERY:
+        result = visitSubquery(context, (SubqueryEval) evalNode, stack);
+        break;
+
       default:
         throw new TajoInternalError("Unknown EvalType: " + evalNode);
       }
@@ -170,4 +174,8 @@ public abstract class SimpleEvalNodeVisitor<CONTEXT> {
   protected EvalNode visitFuncCall(CONTEXT context, FunctionEval evalNode, Stack<EvalNode> stack) {
     return visitDefaultFunctionEval(context, stack, evalNode);
   }
+
+  protected EvalNode visitSubquery(CONTEXT context, SubqueryEval evalNode, Stack<EvalNode> stack) {
+    return evalNode;
+  }
 }

http://git-wip-us.apache.org/repos/asf/tajo/blob/042c3e88/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SubqueryEval.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SubqueryEval.java b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SubqueryEval.java
new file mode 100644
index 0000000..98c36a1
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/expr/SubqueryEval.java
@@ -0,0 +1,99 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.expr;
+
+import org.apache.tajo.annotation.Nullable;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.common.TajoDataTypes.DataType;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.exception.UnsupportedException;
+import org.apache.tajo.plan.logical.TableSubQueryNode;
+import org.apache.tajo.storage.Tuple;
+
+/**
+ * SubqueryEval is a temporal eval to keep subquery information when the subquery occurs in expressions,
+ * such as in subquery or scalar subquery, before {@link org.apache.tajo.plan.rewrite.rules.InSubqueryRewriteRule} is
+ * applied.
+ * During in subquery rewrite phase, A SubqueryEval is expected to be replaced with a Join.
+ *
+ */
+public class SubqueryEval extends ValueSetEval {
+
+  private TableSubQueryNode subQueryNode;
+
+  public SubqueryEval(TableSubQueryNode subQueryNode) {
+    super(EvalType.SUBQUERY);
+    this.subQueryNode = subQueryNode;
+  }
+
+  @Override
+  public DataType getValueType() {
+    return subQueryNode.getOutSchema().getColumn(0).getDataType();
+  }
+
+  @Override
+  public String getName() {
+    return "SUBQUERY";
+  }
+
+  @Override
+  public EvalNode bind(@Nullable EvalContext evalContext, Schema schema) {
+    throw new UnsupportedException("Cannot call bind()");
+  }
+
+  @Override
+  public Datum eval(Tuple tuple) {
+    throw new UnsupportedException("Cannot call eval()");
+  }
+
+  public TableSubQueryNode getSubQueryNode() {
+    return subQueryNode;
+  }
+
+  @Override
+  public int hashCode() {
+    return subQueryNode.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof SubqueryEval) {
+      SubqueryEval other = (SubqueryEval) o;
+      return this.subQueryNode.equals(other.subQueryNode);
+    }
+    return false;
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SubqueryEval clone = (SubqueryEval) super.clone();
+    clone.subQueryNode = (TableSubQueryNode) this.subQueryNode.clone();
+    return clone;
+  }
+
+  @Override
+  public String toString() {
+    return subQueryNode.toString();
+  }
+
+  @Override
+  public Datum[] getValues() {
+    throw new UnsupportedException("Cannot call getValues()");
+  }
+}