tajo-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From hyun...@apache.org
Subject [05/28] TAJO-1125: Separate logical plan and optimizer into a maven module.
Date Sat, 25 Oct 2014 18:17:54 GMT
http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/JoinNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/JoinNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/JoinNode.java
new file mode 100644
index 0000000..58dfac2
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/JoinNode.java
@@ -0,0 +1,163 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.BinaryEval;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.util.TUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class JoinNode extends BinaryNode implements Projectable, Cloneable {
+  @Expose private JoinType joinType;
+  @Expose private EvalNode joinQual;
+  @Expose private Target[] targets;
+
+  // transition states
+  private boolean candidateBroadcast = false;
+  private List<LogicalNode> broadcastCandidateTargets = new ArrayList<LogicalNode>();
+
+  public JoinNode(int pid) {
+    super(pid, NodeType.JOIN);
+  }
+
+  public void init(JoinType joinType, LogicalNode left, LogicalNode right) {
+    this.joinType = joinType;
+    setLeftChild(left);
+    setRightChild(right);
+  }
+
+  public boolean isCandidateBroadcast() {
+    return candidateBroadcast;
+  }
+
+  public void setCandidateBroadcast(boolean candidateBroadcast) {
+    this.candidateBroadcast = candidateBroadcast;
+  }
+
+  public List<LogicalNode> getBroadcastCandidateTargets() {
+    return broadcastCandidateTargets;
+  }
+
+  public JoinType getJoinType() {
+    return this.joinType;
+  }
+
+  public void setJoinType(JoinType joinType) {
+    this.joinType = joinType;
+  }
+
+  public void setJoinQual(EvalNode joinQual) {
+    this.joinQual = joinQual;
+  }
+
+  public boolean hasJoinQual() {
+    return this.joinQual != null;
+  }
+
+  public EvalNode getJoinQual() {
+    return this.joinQual;
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return this.targets != null;
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return this.targets;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    this.setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this).appendTitle("(").appendTitle(joinType.name()).appendTitle(")");
+    if (hasJoinQual()) {
+      planStr.addExplan("Join Cond: " + joinQual.toString());
+    }
+
+    if (hasTargets()) {
+      planStr.addExplan("target list: ");
+      boolean first = true;
+      for (Target target : targets) {
+        if (!first) {
+          planStr.appendExplain(", ");
+        }
+        planStr.appendExplain(target.toString());
+        first = false;
+      }
+    }
+
+    planStr.addDetail("out schema: " + getOutSchema());
+    planStr.addDetail("in schema: " + getInSchema());
+
+    return planStr;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof JoinNode) {
+      JoinNode other = (JoinNode) obj;
+      boolean eq = this.joinType.equals(other.joinType);
+      eq &= TUtil.checkEquals(this.targets, other.targets);
+      eq &= TUtil.checkEquals(joinQual, other.joinQual);
+      return eq && leftChild.equals(other.leftChild) && rightChild.equals(other.rightChild);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    JoinNode join = (JoinNode) super.clone();
+    join.joinType = this.joinType;
+    join.joinQual = this.joinQual == null ? null : (BinaryEval) this.joinQual.clone();
+    if (hasTargets()) {
+      join.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        join.targets[i] = (Target) targets[i].clone();
+      }
+    }
+    return join;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Join (type").append(joinType);
+    if (hasJoinQual()) {
+      sb.append(",filter=").append(joinQual);
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LimitNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LimitNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LimitNode.java
new file mode 100644
index 0000000..7a3431e
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LimitNode.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+
+public final class LimitNode extends UnaryNode implements Cloneable {
+	@Expose private long fetchFirstNum;
+
+  public LimitNode(int pid) {
+    super(pid, NodeType.LIMIT);
+  }
+
+  public void setFetchFirst(long num) {
+    this.fetchFirstNum = num;
+  }
+  
+  public long getFetchFirstNum() {
+    return fetchFirstNum;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(" " + fetchFirstNum);
+  }
+  
+  @Override 
+  public boolean equals(Object obj) {
+    if (obj instanceof LimitNode) {
+      LimitNode other = (LimitNode) obj;
+      return super.equals(other)
+          && fetchFirstNum == other.fetchFirstNum;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    LimitNode newLimitNode = (LimitNode) super.clone();
+    newLimitNode.fetchFirstNum = fetchFirstNum;
+    return newLimitNode;
+  }
+
+  public String toString() {
+    return "Limit (fetch first=" + fetchFirstNum + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNode.java
new file mode 100644
index 0000000..c42a05e
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNode.java
@@ -0,0 +1,128 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.json.GsonObject;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.serder.PlanGsonHelper;
+import org.apache.tajo.util.TUtil;
+
+public abstract class LogicalNode implements Cloneable, GsonObject {
+  @Expose private int pid;
+  @Expose private NodeType type;
+	@Expose private Schema inputSchema;
+	@Expose	private Schema outputSchema;
+
+	@Expose	private double cost = 0;
+
+	protected LogicalNode(int pid, NodeType type) {
+    this.pid = pid;
+    this.type = type;
+	}
+
+  public int getPID() {
+    return pid;
+  }
+
+  public void setPID(int pid) {
+    this.pid = pid;
+  }
+	
+	public NodeType getType() {
+		return this.type;
+	}
+
+	public void setType(NodeType type) {
+		this.type = type;
+	}
+
+	public double getCost() {
+		return this.cost;
+	}
+
+	public void setCost(double cost) {
+		this.cost = cost;
+	}
+	
+	public void setInSchema(Schema inSchema) {
+	  this.inputSchema = inSchema;
+	}
+	
+	public Schema getInSchema() {
+	  return this.inputSchema;
+	}
+	
+	public void setOutSchema(Schema outSchema) {
+	  this.outputSchema = outSchema;
+	}
+	
+	public Schema getOutSchema() {
+	  return this.outputSchema;
+	}
+	
+	@Override
+	public boolean equals(Object obj) {
+	  if (obj instanceof LogicalNode) {
+	    LogicalNode other = (LogicalNode) obj;
+
+      boolean eq = this.type == other.type;
+      eq = eq && TUtil.checkEquals(this.inputSchema, other.inputSchema);
+      eq = eq && TUtil.checkEquals(this.outputSchema, other.outputSchema);
+      eq = eq && this.cost == other.cost;
+
+      return eq;
+	  } else {
+	    return false;
+	  }
+  }
+
+  public boolean deepEquals(Object o) {
+    return equals(o);
+  }
+
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+	  LogicalNode node = (LogicalNode)super.clone();
+    node.pid = pid;
+	  node.type = type;
+	  node.inputSchema =  (Schema) (inputSchema != null ? inputSchema.clone() : null);
+	  node.outputSchema = (Schema) (outputSchema != null ? outputSchema.clone() : null);
+	  return node;
+	}
+
+  @Override
+  public String toJson() {
+    return PlanGsonHelper.toJson(this, LogicalNode.class);
+  }
+
+	public abstract void preOrder(LogicalNodeVisitor visitor);
+  public abstract void postOrder(LogicalNodeVisitor visitor);
+
+  public abstract PlanString getPlanString();
+
+  public String toString() {
+    return PlannerUtil.buildExplainString(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNodeVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNodeVisitor.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNodeVisitor.java
new file mode 100644
index 0000000..b84ec65
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalNodeVisitor.java
@@ -0,0 +1,27 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.plan.logical;
+
+
+public interface LogicalNodeVisitor {
+  void visit(LogicalNode node);
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalRootNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalRootNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalRootNode.java
new file mode 100644
index 0000000..011a9b7
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/LogicalRootNode.java
@@ -0,0 +1,41 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import org.apache.tajo.plan.PlanString;
+
+public class LogicalRootNode extends UnaryNode implements Cloneable {
+  public LogicalRootNode(int pid) {
+    super(pid, NodeType.ROOT);
+  }
+  
+  public String toString() {
+    return "Logical Plan Root\n\n" + getChild().toString();
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    return super.clone();
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NoSuchColumnException.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NoSuchColumnException.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NoSuchColumnException.java
new file mode 100644
index 0000000..94cef1e
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NoSuchColumnException.java
@@ -0,0 +1,34 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+
+import org.apache.tajo.plan.verifier.VerifyException;
+
+public class NoSuchColumnException extends VerifyException {
+  private static final long serialVersionUID = 277182608283894937L;
+
+  public NoSuchColumnException(String databaseName, String relName, String columnName) {
+    super(String.format("ERROR: column \" %s.%s \" in %s does not exist", relName, columnName, databaseName));
+  }
+
+  public NoSuchColumnException(String columnName) {
+    super("ERROR: column \"" + columnName + "\" does not exist");
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NodeType.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NodeType.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NodeType.java
new file mode 100644
index 0000000..5d1874f
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/NodeType.java
@@ -0,0 +1,67 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+package org.apache.tajo.plan.logical;
+
+
+/**
+ * This indicates a logical node type.
+ */
+public enum NodeType {
+  ROOT(LogicalRootNode.class),
+  EXPRS(EvalExprNode.class),
+  PROJECTION(ProjectionNode.class),
+  LIMIT(LimitNode.class),
+  SORT(SortNode.class),
+  HAVING(HavingNode.class),
+  GROUP_BY(GroupbyNode.class),
+  WINDOW_AGG(WindowAggNode.class),
+  SELECTION(SelectionNode.class),
+  JOIN(JoinNode.class),
+  UNION(UnionNode.class),
+  EXCEPT(ExceptNode.class),
+  INTERSECT(IntersectNode.class),
+  TABLE_SUBQUERY(TableSubQueryNode.class),
+  SCAN(ScanNode.class),
+  PARTITIONS_SCAN(PartitionedTableScanNode.class),
+  BST_INDEX_SCAN(IndexScanNode.class),
+  STORE(StoreTableNode.class),
+  INSERT(InsertNode.class),
+  DISTINCT_GROUP_BY(DistinctGroupbyNode.class),
+
+  CREATE_DATABASE(CreateDatabaseNode.class),
+  DROP_DATABASE(DropDatabaseNode.class),
+  CREATE_TABLE(CreateTableNode.class),
+  DROP_TABLE(DropTableNode.class),
+  ALTER_TABLESPACE (AlterTablespaceNode.class),
+  ALTER_TABLE (AlterTableNode.class),
+  TRUNCATE_TABLE (TruncateTableNode.class);
+
+  private final Class<? extends LogicalNode> baseClass;
+
+  NodeType(Class<? extends LogicalNode> baseClass) {
+    this.baseClass = baseClass;
+  }
+
+  public Class<? extends LogicalNode> getBaseClass() {
+    return this.baseClass;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PartitionedTableScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PartitionedTableScanNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PartitionedTableScanNode.java
new file mode 100644
index 0000000..6fd969a
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PartitionedTableScanNode.java
@@ -0,0 +1,159 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.util.TUtil;
+
+public class PartitionedTableScanNode extends ScanNode {
+  @Expose Path [] inputPaths;
+
+  public PartitionedTableScanNode(int pid) {
+    super(pid, NodeType.PARTITIONS_SCAN);
+  }
+
+  public void init(ScanNode scanNode, Path[] inputPaths) {
+    tableDesc = scanNode.tableDesc;
+    setInSchema(scanNode.getInSchema());
+    setOutSchema(scanNode.getOutSchema());
+    this.qual = scanNode.qual;
+    this.targets = scanNode.targets;
+    this.inputPaths = inputPaths;
+
+    if (scanNode.hasAlias()) {
+      alias = scanNode.alias;
+    }
+  }
+
+  public void setInputPaths(Path [] paths) {
+    this.inputPaths = paths;
+  }
+
+  public Path [] getInputPaths() {
+    return inputPaths;
+  }
+	
+	public String toString() {
+    StringBuilder sb = new StringBuilder("Partitions Scan (table=").append(getTableName());
+    if (hasAlias()) {
+      sb.append(", alias=").append(alias);
+    }
+    if (hasQual()) {
+      sb.append(", filter=").append(qual);
+    }
+    sb.append(", path=").append(getTableDesc().getPath()).append(")");
+	  return sb.toString();
+	}
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.tableDesc, this.qual, this.targets);
+  }
+	
+	@Override
+	public boolean equals(Object obj) {
+	  if (obj instanceof PartitionedTableScanNode) {
+	    PartitionedTableScanNode other = (PartitionedTableScanNode) obj;
+	    
+	    boolean eq = super.equals(other); 
+	    eq = eq && TUtil.checkEquals(this.tableDesc, other.tableDesc);
+	    eq = eq && TUtil.checkEquals(this.qual, other.qual);
+	    eq = eq && TUtil.checkEquals(this.targets, other.targets);
+      eq = eq && TUtil.checkEquals(this.inputPaths, other.inputPaths);
+	    
+	    return eq;
+	  }	  
+	  
+	  return false;
+	}	
+	
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+	  PartitionedTableScanNode unionScan = (PartitionedTableScanNode) super.clone();
+	  
+	  unionScan.tableDesc = (TableDesc) this.tableDesc.clone();
+	  
+	  if (hasQual()) {
+	    unionScan.qual = (EvalNode) this.qual.clone();
+	  }
+	  
+	  if (hasTargets()) {
+	    unionScan.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        unionScan.targets[i] = (Target) targets[i].clone();
+      }
+	  }
+
+    unionScan.inputPaths = inputPaths;
+
+    return unionScan;
+	}
+	
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+	
+	public void postOrder(LogicalNodeVisitor visitor) {        
+    visitor.visit(this);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this).appendTitle(" on " + getTableName());
+    if (hasAlias()) {
+      planStr.appendTitle(" as ").appendTitle(alias);
+    }
+
+    if (hasQual()) {
+      planStr.addExplan("filter: ").appendExplain(this.qual.toString());
+    }
+
+    if (hasTargets()) {
+      planStr.addExplan("target list: ");
+      boolean first = true;
+      for (Target target : targets) {
+        if (!first) {
+          planStr.appendExplain(", ");
+        }
+        planStr.appendExplain(target.toString());
+        first = false;
+      }
+    }
+
+    planStr.addDetail("out schema: ").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema: ").appendDetail(getInSchema().toString());
+
+    if (inputPaths != null) {
+      planStr.addExplan("num of filtered paths: ").appendExplain(""+ inputPaths.length);
+      int i = 0;
+      for (Path path : inputPaths) {
+        planStr.addDetail((i++) + ": ").appendDetail(path.toString());
+      }
+    }
+
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PersistentStoreNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PersistentStoreNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PersistentStoreNode.java
new file mode 100644
index 0000000..420ed7c
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/PersistentStoreNode.java
@@ -0,0 +1,90 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.KeyValueSet;
+import org.apache.tajo.util.TUtil;
+
+import static org.apache.tajo.catalog.proto.CatalogProtos.StoreType;
+
+
+/**
+ * <code>PersistentStoreNode</code> an expression for a persistent data store step.
+ * This includes some basic information for materializing data.
+ */
+public abstract class PersistentStoreNode extends UnaryNode implements Cloneable {
+  @Expose protected StoreType storageType = StoreType.CSV;
+  @Expose protected KeyValueSet options = new KeyValueSet();
+
+  protected PersistentStoreNode(int pid, NodeType nodeType) {
+    super(pid, nodeType);
+  }
+
+  public void setStorageType(StoreType storageType) {
+    this.storageType = storageType;
+  }
+
+  public StoreType getStorageType() {
+    return this.storageType;
+  }
+
+  public boolean hasOptions() {
+    return this.options != null;
+  }
+
+  public KeyValueSet getOptions() {
+    return this.options;
+  }
+
+  public void setOptions(KeyValueSet options) {
+    this.options = options;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.addExplan("Store type: " + storageType);
+
+    return planStr;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof PersistentStoreNode) {
+      PersistentStoreNode other = (PersistentStoreNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && this.storageType.equals(other.storageType);
+      eq = eq && TUtil.checkEquals(options, other.options);
+      return eq;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    PersistentStoreNode store = (PersistentStoreNode) super.clone();
+    store.storageType = storageType != null ? storageType : null;
+    store.options = options != null ? (KeyValueSet) options.clone() : null;
+    return store;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/Projectable.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/Projectable.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/Projectable.java
new file mode 100644
index 0000000..68d1861
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/Projectable.java
@@ -0,0 +1,73 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.plan.Target;
+
+/**
+ * Projectable is an interface for a LogicalNode which has a list of targets.
+ * What a logical node has a list of targets means that the node evaluated a list of expressions.
+ * For example, {@link ScanNode},
+ * {@link JoinNode},
+ * {@link GroupbyNode}, and
+ * {@link ProjectionNode} are all <i>Projectable</i> nodes.
+ * The expression evaluation occurs only at those projectable nodes.
+ */
+public interface Projectable {
+
+  /**
+   * Get a PlanNode Id
+   * @return PlanNodeId
+   */
+  int getPID();
+
+  /**
+   * check if this node has a target list
+   * @return TRUE if this node has a target list. Otherwise, FALSE.
+   */
+  boolean hasTargets();
+
+  /**
+   * Set a target list
+   *
+   * @param targets The array of targets
+   */
+  void setTargets(Target[] targets);
+
+  /**
+   * Get a list of targets
+   *
+   * @return The array of targets
+   */
+  Target [] getTargets();
+
+  /**
+   * Get an input schema
+   * @return The input schema
+   */
+  public Schema getInSchema();
+
+  /**
+   * Get an output schema
+   *
+   * @return The output schema
+   */
+  public Schema getOutSchema();
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ProjectionNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ProjectionNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ProjectionNode.java
new file mode 100644
index 0000000..4ef7e2d
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ProjectionNode.java
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.util.TUtil;
+
+public class ProjectionNode extends UnaryNode implements Projectable {
+  /**
+   * the targets are always filled even if the query is 'select *'
+   */
+  @Expose	private Target [] targets;
+  @Expose private boolean distinct = false;
+
+	public ProjectionNode(int pid) {
+		super(pid, NodeType.PROJECTION);
+	}
+
+  public boolean hasTargets() {
+    return this.targets != null;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    this.setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target [] getTargets() {
+    return this.targets;
+  }
+	
+	public void setChild(LogicalNode subNode) {
+	  super.setChild(subNode);
+	}
+	
+	public String toString() {
+	  StringBuilder sb = new StringBuilder("Projection (distinct=").append(distinct);
+    if (targets != null) {
+      sb.append(", exprs=").append(TUtil.arrayToString(targets)).append(")");
+    }
+	  return sb.toString();
+	}
+	
+	@Override
+  public boolean equals(Object obj) {
+	  if (obj instanceof ProjectionNode) {
+	    ProjectionNode other = (ProjectionNode) obj;
+	    
+	    boolean b1 = super.equals(other);
+      boolean b2 = TUtil.checkEquals(targets, other.targets);
+      return b1 && b2;
+	  } else {
+	    return false;
+	  }
+	}
+
+	@Override
+  public Object clone() throws CloneNotSupportedException {
+	  ProjectionNode projNode = (ProjectionNode) super.clone();
+	  projNode.targets = targets.clone();
+	  
+	  return projNode;
+	}
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+
+    if (distinct) {
+      planStr.appendTitle(" (distinct)");
+    }
+
+
+    StringBuilder sb = new StringBuilder("Targets: ");
+    if (targets != null) {
+      for (int i = 0; i < targets.length; i++) {
+        sb.append(targets[i]);
+        if (i < targets.length - 1) {
+          sb.append(", ");
+        }
+      }
+    }
+    planStr.addExplan(sb.toString());
+    if (getOutSchema() != null) {
+      planStr.addExplan("out schema: " + getOutSchema().toString());
+    }
+    if (getInSchema() != null) {
+      planStr.addExplan("in  schema: " + getInSchema().toString());
+    }
+
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/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
new file mode 100644
index 0000000..fd8e937
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/RelationNode.java
@@ -0,0 +1,49 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import org.apache.tajo.catalog.Schema;
+
+/**
+ * It provides a logical view of a relation. Regarding a table, the main difference between a logical view and a
+ * physical view is as follows:
+ *
+ * <ul>
+ * <li>In logical view, each column in the table has qualified name by table alias name. In addition, the schema of
+ * logical view will includes partition columns if we use column-partitioned tables.</li>
+ * <li>In contrast, in physical view: each column in the table has qualified name by the original table.</li>
+ * </ul>
+ */
+public abstract class RelationNode extends LogicalNode {
+
+  protected RelationNode(int pid, NodeType nodeType) {
+    super(pid, nodeType);
+    assert(nodeType == NodeType.SCAN || nodeType == NodeType.PARTITIONS_SCAN || nodeType == NodeType.TABLE_SUBQUERY);
+  }
+
+  public abstract boolean hasAlias();
+
+  public abstract String getAlias();
+
+  public abstract String getTableName();
+
+  public abstract String getCanonicalName();
+
+  public abstract Schema getTableSchema();
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ScanNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ScanNode.java
new file mode 100644
index 0000000..a7ef543
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ScanNode.java
@@ -0,0 +1,247 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.util.SchemaUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.util.TUtil;
+
+public class ScanNode extends RelationNode implements Projectable, SelectableNode, Cloneable {
+	@Expose protected TableDesc tableDesc;
+  @Expose protected String alias;
+  @Expose protected Schema logicalSchema;
+	@Expose protected EvalNode qual;
+	@Expose protected Target[] targets;
+  @Expose protected boolean broadcastTable;
+
+  protected ScanNode(int pid, NodeType nodeType) {
+    super(pid, nodeType);
+  }
+
+  public ScanNode(int pid) {
+    super(pid, NodeType.SCAN);
+  }
+
+  public void init(TableDesc desc) {
+    this.tableDesc = desc;
+    this.setInSchema(tableDesc.getSchema());
+    this.setOutSchema(tableDesc.getSchema());
+    logicalSchema = SchemaUtil.getQualifiedLogicalSchema(tableDesc, null);
+  }
+  
+	public void init(TableDesc desc, String alias) {
+    this.tableDesc = desc;
+    this.alias = alias;
+
+    if (!CatalogUtil.isFQTableName(this.tableDesc.getName())) {
+      throw new IllegalArgumentException("the name in TableDesc must be qualified, but it is \"" +
+          desc.getName() + "\"");
+    }
+
+    String databaseName = CatalogUtil.extractQualifier(this.tableDesc.getName());
+    String qualifiedAlias = CatalogUtil.buildFQName(databaseName, alias);
+    this.setInSchema(tableDesc.getSchema());
+    this.getInSchema().setQualifier(qualifiedAlias);
+    this.setOutSchema(new Schema(getInSchema()));
+    logicalSchema = SchemaUtil.getQualifiedLogicalSchema(tableDesc, qualifiedAlias);
+	}
+	
+	public String getTableName() {
+	  return tableDesc.getName();
+	}
+
+  @Override
+	public boolean hasAlias() {
+	  return alias != null;
+	}
+
+  @Override
+  public String getAlias() {
+    return alias;
+  }
+
+  public void setBroadcastTable(boolean broadcastTable) {
+    this.broadcastTable = broadcastTable;
+  }
+
+  public boolean isBroadcastTable() {
+    return broadcastTable;
+  }
+
+  public String getCanonicalName() {
+    if (CatalogUtil.isFQTableName(this.tableDesc.getName())) {
+      String databaseName = CatalogUtil.extractQualifier(this.tableDesc.getName());
+      return hasAlias() ? CatalogUtil.buildFQName(databaseName, alias) : tableDesc.getName();
+    } else {
+      return hasAlias() ? alias : tableDesc.getName();
+    }
+  }
+
+  @Override
+  public Schema getTableSchema() {
+    return logicalSchema;
+  }
+
+  public Schema getPhysicalSchema() {
+    return getInSchema();
+  }
+
+  @Override
+	public boolean hasQual() {
+	  return qual != null;
+	}
+
+  @Override
+	public EvalNode getQual() {
+	  return this.qual;
+	}
+
+  @Override
+	public void setQual(EvalNode evalTree) {
+	  this.qual = evalTree;
+	}
+
+  @Override
+	public boolean hasTargets() {
+	  return this.targets != null;
+	}
+
+  @Override
+	public void setTargets(Target [] targets) {
+	  this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+	}
+
+  @Override
+	public Target [] getTargets() {
+	  return this.targets;
+	}
+
+  public TableDesc getTableDesc() {
+    return tableDesc;
+  }
+	
+	public String toString() {
+    StringBuilder sb = new StringBuilder("Scan (table=").append(getTableName());
+    if (hasAlias()) {
+      sb.append(", alias=").append(alias);
+    }
+    if (hasQual()) {
+      sb.append(", filter=").append(qual);
+    }
+    sb.append(", path=").append(getTableDesc().getPath()).append(")");
+    return sb.toString();
+	}
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(this.tableDesc, this.qual, this.targets);
+  }
+	
+	@Override
+	public boolean equals(Object obj) {
+	  if (obj instanceof ScanNode) {
+	    ScanNode other = (ScanNode) obj;
+	    
+	    boolean eq = super.equals(other); 
+	    eq = eq && TUtil.checkEquals(this.tableDesc, other.tableDesc);
+	    eq = eq && TUtil.checkEquals(this.qual, other.qual);
+	    eq = eq && TUtil.checkEquals(this.targets, other.targets);
+	    
+	    return eq;
+	  }	  
+	  
+	  return false;
+	}	
+	
+	@Override
+	public Object clone() throws CloneNotSupportedException {
+    ScanNode scanNode = (ScanNode) super.clone();
+
+    scanNode.tableDesc = (TableDesc) this.tableDesc.clone();
+
+    if (hasQual()) {
+      scanNode.qual = (EvalNode) this.qual.clone();
+    }
+
+    if (hasTargets()) {
+      scanNode.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        scanNode.targets[i] = (Target) targets[i].clone();
+      }
+    }
+
+    if (hasAlias()) {
+      scanNode.alias = alias;
+    }
+
+    return scanNode;
+	}
+	
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+	
+	public void postOrder(LogicalNodeVisitor visitor) {        
+    visitor.visit(this);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this).appendTitle(" on ").appendTitle(getTableName());
+    if (hasAlias()) {
+      planStr.appendTitle(" as ").appendTitle(alias);
+    }
+
+    if (hasQual()) {
+      planStr.addExplan("filter: ").appendExplain(this.qual.toString());
+    }
+
+    if (hasTargets()) {
+      planStr.addExplan("target list: ");
+      boolean first = true;
+      for (Target target : targets) {
+        if (!first) {
+          planStr.appendExplain(", ");
+        }
+        planStr.appendExplain(target.toString());
+        first = false;
+      }
+    }
+
+    planStr.addDetail("out schema: ").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema: ").appendDetail(getInSchema().toString());
+
+    return planStr;
+  }
+
+  public static boolean isScanNode(LogicalNode node) {
+    return node.getType() == NodeType.SCAN ||
+        node.getType() == NodeType.PARTITIONS_SCAN;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectableNode.java
new file mode 100644
index 0000000..1a66d94
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectableNode.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import org.apache.tajo.plan.expr.EvalNode;
+
+/**
+ * An interface for logical node which is able to filter tuples.
+ */
+public interface SelectableNode {
+
+  /**
+   * Checking if it has filter condition
+   *
+   * @return True if it has filter condition. Otherwise, it will return false.
+   */
+  public boolean hasQual();
+
+  /**
+   * Set a filter condition.
+   *
+   * @param eval EvalNode resulting in a boolean result.
+   */
+  public void setQual(EvalNode eval);
+
+  /**
+   * Get a filter condition
+   *
+   * @return Filter Condition
+   */
+  public EvalNode getQual();
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectionNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectionNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectionNode.java
new file mode 100644
index 0000000..4c4d5ee
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SelectionNode.java
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.expr.EvalNode;
+
+public class SelectionNode extends UnaryNode implements SelectableNode, Cloneable {
+	@Expose private EvalNode qual;
+
+  public SelectionNode(int pid) {
+    super(pid, NodeType.SELECTION);
+  }
+
+  @Override
+  public boolean hasQual() {
+    return true;
+  }
+
+  public void setQual(EvalNode qual) {
+		this.qual = qual;
+	}
+
+  public EvalNode getQual() {
+    return this.qual;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.addExplan("Search Cond: " + getQual());
+    return planStr;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof SelectionNode) {
+      SelectionNode other = (SelectionNode) obj;
+      return super.equals(other) 
+          && this.qual.equals(other.qual);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SelectionNode selNode = (SelectionNode) super.clone();
+    selNode.qual = (EvalNode) this.qual.clone();
+    
+    return selNode;
+  }
+
+  public String toString() {
+    return "Selection (filter=" + qual + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ShuffleFileWriteNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ShuffleFileWriteNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ShuffleFileWriteNode.java
new file mode 100644
index 0000000..29aefc9
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/ShuffleFileWriteNode.java
@@ -0,0 +1,103 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Preconditions;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.util.TUtil;
+
+import static org.apache.tajo.plan.serder.PlanProto.ShuffleType;
+
+/**
+ * ShuffeFileWriteNode is an expression for an intermediate data materialization step.
+ */
+public class ShuffleFileWriteNode extends PersistentStoreNode implements Cloneable {
+  @Expose private ShuffleType shuffleType = ShuffleType.NONE_SHUFFLE;
+  @Expose private int numOutputs;
+  @Expose private Column [] shuffleKeys;
+
+  public ShuffleFileWriteNode(int pid) {
+    super(pid, NodeType.STORE);
+  }
+    
+  public final int getNumOutputs() {
+    return this.numOutputs;
+  }
+  
+  public final boolean hasShuffleKeys() {
+    return this.shuffleKeys != null;
+  }
+  
+  public final Column [] getShuffleKeys() {
+    return shuffleKeys;
+  }
+  
+  public final void setShuffle(ShuffleType type, Column[] keys, int numPartitions) {
+    Preconditions.checkArgument(keys.length >= 0, 
+        "At least one partition key must be specified.");
+    // In outer join, zero can be passed into this value because of empty tables.
+    // So, we should allow zero.
+    Preconditions.checkArgument(numPartitions >= 0,
+        "The number of partitions must be positive: %s", numPartitions);
+
+    this.shuffleType = type;
+    this.shuffleKeys = keys;
+    this.numOutputs = numPartitions;
+  }
+
+  public ShuffleType getShuffleType() {
+    return this.shuffleType;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof ShuffleFileWriteNode) {
+      ShuffleFileWriteNode other = (ShuffleFileWriteNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && this.numOutputs == other.numOutputs;
+      eq = eq && TUtil.checkEquals(shuffleKeys, other.shuffleKeys);
+      return eq;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    ShuffleFileWriteNode store = (ShuffleFileWriteNode) super.clone();
+    store.numOutputs = numOutputs;
+    store.shuffleKeys = shuffleKeys != null ? shuffleKeys.clone() : null;
+    return store;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Shuffle Write (type=" + shuffleType.name().toLowerCase());
+    if (storageType != null) {
+      sb.append(", storage="+ storageType.name());
+    }
+    sb.append(", part number=").append(numOutputs);
+    if (shuffleKeys != null) {
+      sb.append(", keys: ").append(TUtil.arrayToString(shuffleKeys));
+    }
+    sb.append(")");
+    
+    return sb.toString();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SortNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SortNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SortNode.java
new file mode 100644
index 0000000..a697f9f
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/SortNode.java
@@ -0,0 +1,94 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Preconditions;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.TUtil;
+
+public final class SortNode extends UnaryNode implements Cloneable {
+	@Expose private SortSpec [] sortKeys;
+
+  public SortNode(int pid) {
+    super(pid, NodeType.SORT);
+  }
+
+  public void setSortSpecs(SortSpec[] sortSpecs) {
+    Preconditions.checkArgument(sortSpecs.length > 0, "At least one sort key must be specified");
+    this.sortKeys = sortSpecs;
+  }
+  
+  public SortSpec[] getSortKeys() {
+    return this.sortKeys;
+  }
+  
+  @Override 
+  public boolean equals(Object obj) {
+    if (obj instanceof SortNode) {
+      SortNode other = (SortNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && TUtil.checkEquals(sortKeys, other.sortKeys);
+      return eq;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SortNode sort = (SortNode) super.clone();
+    sort.sortKeys = sortKeys.clone();
+    
+    return sort;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    StringBuilder sb = new StringBuilder("Sort Keys: ");
+    for (int i = 0; i < sortKeys.length; i++) {
+      sb.append(sortKeys[i].getSortKey().getSimpleName()).append(" ")
+          .append(sortKeys[i].isAscending() ? "asc" : "desc");
+      if( i < sortKeys.length - 1) {
+        sb.append(",");
+      }
+    }
+    planStr.addExplan(sb.toString());
+    return planStr;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Sort [key= ");
+    for (int i = 0; i < sortKeys.length; i++) {    
+      sb.append(sortKeys[i].getSortKey().getQualifiedName()).append(" ")
+          .append(sortKeys[i].isAscending() ? "asc" : "desc");
+      if(i < sortKeys.length - 1) {
+        sb.append(",");
+      }
+    }
+    sb.append("]");
+
+    sb.append("\n\"out schema: " + getOutSchema()
+        + "\n\"in schema: " + getInSchema());
+    return sb.toString()+"\n"
+        + getChild().toString();
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/StoreTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/StoreTableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/StoreTableNode.java
new file mode 100644
index 0000000..0623d21
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/StoreTableNode.java
@@ -0,0 +1,100 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.partition.PartitionMethodDesc;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.TUtil;
+
+public class StoreTableNode extends PersistentStoreNode implements Cloneable {
+  @Expose protected String tableName;
+  @Expose private PartitionMethodDesc partitionDesc;
+
+  public StoreTableNode(int pid) {
+    super(pid, NodeType.STORE);
+  }
+
+  protected StoreTableNode(int pid, NodeType nodeType) {
+    super(pid, nodeType);
+  }
+
+  public boolean hasTargetTable() {
+    return tableName != null;
+  }
+
+  public void setTableName(String tableName) {
+    this.tableName = tableName;
+  }
+
+  public final String getTableName() {
+    return this.tableName;
+  }
+
+  public boolean hasPartition() {
+    return this.partitionDesc != null;
+  }
+
+  public PartitionMethodDesc getPartitionMethod() {
+    return partitionDesc;
+  }
+
+  public void setPartitionMethod(PartitionMethodDesc partitionDesc) {
+    this.partitionDesc = partitionDesc;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.appendTitle(" into ").appendTitle(tableName);
+    planStr.addExplan("Store type: " + storageType);
+
+    return planStr;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof StoreTableNode) {
+      StoreTableNode other = (StoreTableNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && this.tableName.equals(other.tableName);
+      eq = eq && TUtil.checkEquals(partitionDesc, other.partitionDesc);
+      return eq;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    StoreTableNode store = (StoreTableNode) super.clone();
+    store.tableName = tableName;
+    store.partitionDesc = partitionDesc != null ? (PartitionMethodDesc) partitionDesc.clone() : null;
+    return store;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Store Table (table=").append(tableName);
+    if (storageType != null) {
+      sb.append(", storage="+ storageType.name());
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TableSubQueryNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TableSubQueryNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TableSubQueryNode.java
new file mode 100644
index 0000000..c8fbecd
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TableSubQueryNode.java
@@ -0,0 +1,180 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.util.SchemaUtil;
+import org.apache.tajo.plan.Target;
+
+public class TableSubQueryNode extends RelationNode implements Projectable {
+  @Expose private String tableName;
+  @Expose private LogicalNode subQuery;
+  @Expose private Target [] targets; // unused
+
+  public TableSubQueryNode(int pid) {
+    super(pid, NodeType.TABLE_SUBQUERY);
+  }
+
+  public void init(String tableName, LogicalNode subQuery) {
+    this.tableName = tableName;
+    if (subQuery != null) {
+      this.subQuery = subQuery;
+      setOutSchema(SchemaUtil.clone(this.subQuery.getOutSchema()));
+      setInSchema(SchemaUtil.clone(this.subQuery.getOutSchema()));
+      getInSchema().setQualifier(this.tableName);
+      getOutSchema().setQualifier(this.tableName);
+    }
+  }
+
+  @Override
+  public boolean hasAlias() {
+    return false;
+  }
+
+  @Override
+  public String getAlias() {
+    return null;
+  }
+
+  public String getTableName() {
+    return tableName;
+  }
+
+  @Override
+  public String getCanonicalName() {
+    return tableName;
+  }
+
+  @Override
+  public Schema getTableSchema() {
+    // an output schema can be determined by targets. So, an input schema of
+    // TableSubQueryNode is only eligible for table schema.
+    //
+    // TODO - but, a derived table can have column alias. For that, we should improve here.
+    //
+    // example) select * from (select col1, col2, col3 from t1) view (c1, c2);
+
+    return getInSchema();
+  }
+
+  public void setSubQuery(LogicalNode node) {
+    this.subQuery = node;
+    setInSchema(SchemaUtil.clone(this.subQuery.getOutSchema()));
+    getInSchema().setQualifier(this.tableName);
+    if (hasTargets()) {
+      setOutSchema(PlannerUtil.targetToSchema(targets));
+    } else {
+      setOutSchema(SchemaUtil.clone(this.subQuery.getOutSchema()));
+    }
+  }
+
+  public LogicalNode getSubQuery() {
+    return subQuery;
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return targets != null;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return targets;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    planStr.appendTitle(" as ").appendTitle(tableName);
+
+    if (hasTargets()) {
+      StringBuilder sb = new StringBuilder("Targets: ");
+      for (int i = 0; i < targets.length; i++) {
+        sb.append(targets[i]);
+        if( i < targets.length - 1) {
+          sb.append(", ");
+        }
+      }
+      planStr.addExplan(sb.toString());
+      if (getOutSchema() != null) {
+        planStr.addExplan("out schema: " + getOutSchema().toString());
+      }
+      if (getInSchema() != null) {
+        planStr.addExplan("in  schema: " + getInSchema().toString());
+      }
+    }
+
+    return planStr;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(tableName, subQuery);
+  }
+
+  @Override
+  public boolean equals(Object object) {
+    if (object instanceof TableSubQueryNode) {
+      TableSubQueryNode another = (TableSubQueryNode) object;
+      return tableName.equals(another.tableName) && subQuery.equals(another.subQuery);
+    }
+
+    return false;
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    TableSubQueryNode newTableSubQueryNode = (TableSubQueryNode) super.clone();
+    newTableSubQueryNode.tableName = tableName;
+    newTableSubQueryNode.subQuery = (LogicalNode) subQuery.clone();
+    if (hasTargets()) {
+      newTableSubQueryNode.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        newTableSubQueryNode.targets[i] = (Target) targets[i].clone();
+      }
+    }
+    return newTableSubQueryNode;
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+    subQuery.preOrder(visitor);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    subQuery.preOrder(visitor);
+    visitor.visit(this);
+  }
+
+  public String toString() {
+    return "Inline view (name=" + tableName + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TruncateTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TruncateTableNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TruncateTableNode.java
new file mode 100644
index 0000000..10c65b6
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/TruncateTableNode.java
@@ -0,0 +1,62 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.util.TUtil;
+
+import java.util.List;
+
+public class TruncateTableNode extends LogicalNode {
+  @Expose
+  private List<String> tableNames;
+
+  public TruncateTableNode(int pid) {
+    super(pid, NodeType.TRUNCATE_TABLE);
+  }
+
+  public List<String> getTableNames() {
+    return tableNames;
+  }
+
+  public void setTableNames(List<String> tableNames) {
+    this.tableNames = tableNames;
+  }
+
+  @Override
+  public String toString() {
+    return "TruncateTable (table=" + TUtil.collectionToString(tableNames, ", ") + ")";
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnaryNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnaryNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnaryNode.java
new file mode 100644
index 0000000..0fc5c37
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnaryNode.java
@@ -0,0 +1,69 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.util.TUtil;
+
+
+public abstract class UnaryNode extends LogicalNode implements Cloneable {
+	@Expose LogicalNode child;
+	
+	/**
+	 * @param type
+	 */
+	public UnaryNode(int pid, NodeType type) {
+		super(pid, type);
+	}
+	
+	public void setChild(LogicalNode subNode) {
+		this.child = subNode;
+	}
+	
+	public <T extends LogicalNode> T getChild() {
+		return (T) this.child;
+	}
+
+  @Override
+  public boolean deepEquals(Object o) {
+    if (o instanceof UnaryNode) {
+      UnaryNode u = (UnaryNode) o;
+      return equals(o) && TUtil.checkEquals(child, u.child);
+    }
+    return false;
+  }
+	
+	@Override
+  public Object clone() throws CloneNotSupportedException {
+	  UnaryNode unary = (UnaryNode) super.clone();
+	  unary.child = (LogicalNode) (child == null ? null : child.clone());
+	  
+	  return unary;
+	}
+	
+	public void preOrder(LogicalNodeVisitor visitor) {
+	  visitor.visit(this);
+	  child.preOrder(visitor);
+  }
+	
+	public void postOrder(LogicalNodeVisitor visitor) {
+	  child.postOrder(visitor);
+	  visitor.visit(this);
+	}
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnionNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnionNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnionNode.java
new file mode 100644
index 0000000..2839ab5
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/UnionNode.java
@@ -0,0 +1,37 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.plan.logical;
+
+import org.apache.tajo.plan.PlanString;
+
+public class UnionNode extends BinaryNode {
+
+  public UnionNode(int pid) {
+    super(pid, NodeType.UNION);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowAggNode.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowAggNode.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowAggNode.java
new file mode 100644
index 0000000..3f624f6
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowAggNode.java
@@ -0,0 +1,239 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.plan.PlanString;
+import org.apache.tajo.plan.util.PlannerUtil;
+import org.apache.tajo.plan.Target;
+import org.apache.tajo.plan.expr.WindowFunctionEval;
+import org.apache.tajo.util.TUtil;
+
+public class WindowAggNode extends UnaryNode implements Projectable, Cloneable {
+	/** partition key sets */
+  @Expose private Column [] partitionKeys;
+  /** order key sets */
+  @Expose private SortSpec [] sortSpecs;
+
+  /** Aggregation Functions */
+  @Expose private WindowFunctionEval[] windowFuncs;
+  /**
+   * It's a list of targets. The partition key columns should be followed by window functions.
+   * aggrFunctions keep actual aggregation functions, but it only contains field references.
+   * */
+  @Expose private Target [] targets;
+  @Expose private boolean hasDistinct = false;
+
+  public WindowAggNode(int pid) {
+    super(pid, NodeType.WINDOW_AGG);
+  }
+
+  public final boolean hasPartitionKeys() {
+    return partitionKeys != null && partitionKeys.length > 0;
+  }
+
+  public void setPartitionKeys(Column[] groupingColumns) {
+    this.partitionKeys = groupingColumns;
+  }
+
+	public final Column [] getPartitionKeys() {
+	  return this.partitionKeys;
+	}
+
+  public final boolean hasSortSpecs() {
+    return this.sortSpecs != null;
+  }
+
+  public void setSortSpecs(SortSpec [] sortSpecs) {
+    this.sortSpecs = sortSpecs;
+  }
+
+  public final SortSpec [] getSortSpecs() {
+    return this.sortSpecs;
+  }
+
+  public final boolean isDistinct() {
+    return hasDistinct;
+  }
+
+  public void setDistinct(boolean distinct) {
+    hasDistinct = distinct;
+  }
+
+  public boolean hasAggFunctions() {
+    return this.windowFuncs != null;
+  }
+
+  public WindowFunctionEval[] getWindowFunctions() {
+    return this.windowFuncs;
+  }
+
+  public void setWindowFunctions(WindowFunctionEval[] evals) {
+    this.windowFuncs = evals;
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return this.targets != null;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return this.targets;
+  }
+  
+  public void setChild(LogicalNode subNode) {
+    super.setChild(subNode);
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder("WinAgg (");
+    if (hasPartitionKeys()) {
+      sb.append("partition keys=").append(TUtil.arrayToString(partitionKeys));
+      sb.append(", ");
+    }
+    if (hasAggFunctions()) {
+      sb.append("funcs=").append(TUtil.arrayToString(windowFuncs));
+    }
+    if (hasSortSpecs()) {
+      sb.append("sort=").append(TUtil.arrayToString(sortSpecs));
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof WindowAggNode) {
+      WindowAggNode other = (WindowAggNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && TUtil.checkEquals(partitionKeys, other.partitionKeys);
+      eq = eq && TUtil.checkEquals(sortSpecs, other.sortSpecs);
+      eq = eq && TUtil.checkEquals(windowFuncs, other.windowFuncs);
+      eq = eq && TUtil.checkEquals(targets, other.targets);
+      eq = eq && TUtil.checkEquals(hasDistinct, other.hasDistinct);
+      return eq;
+    } else {
+      return false;  
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(partitionKeys, sortSpecs, windowFuncs, targets, hasDistinct);
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    WindowAggNode grp = (WindowAggNode) super.clone();
+    if (partitionKeys != null) {
+      grp.partitionKeys = new Column[partitionKeys.length];
+      for (int i = 0; i < partitionKeys.length; i++) {
+        grp.partitionKeys[i] = partitionKeys[i];
+      }
+    }
+
+    if (windowFuncs != null) {
+      grp.windowFuncs = new WindowFunctionEval[windowFuncs.length];
+      for (int i = 0; i < windowFuncs.length; i++) {
+        grp.windowFuncs[i] = (WindowFunctionEval) windowFuncs[i].clone();
+      }
+    }
+
+    if (targets != null) {
+      grp.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        grp.targets[i] = (Target) targets[i].clone();
+      }
+    }
+
+    return grp;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("(");
+    if (hasPartitionKeys()) {
+      sb.append("PARTITION BY ");
+      for (int j = 0; j < partitionKeys.length; j++) {
+        sb.append(partitionKeys[j].getSimpleName());
+        if(j < partitionKeys.length - 1) {
+          sb.append(",");
+        }
+      }
+    }
+
+    if (hasSortSpecs()) {
+      sb.append("ORDER BY ");
+      for (int i = 0; i < sortSpecs.length; i++) {
+        sb.append(sortSpecs[i].getSortKey().getSimpleName()).append(" ")
+            .append(sortSpecs[i].isAscending() ? "asc" : "desc");
+        if( i < sortSpecs.length - 1) {
+          sb.append(",");
+        }
+
+      }
+    }
+
+    sb.append(")");
+
+    planStr.appendTitle(sb.toString());
+
+    // there can be no aggregation functions
+    if (hasAggFunctions()) {
+      sb = new StringBuilder();
+      sb.append("(");
+
+      for (int j = 0; j < windowFuncs.length; j++) {
+        sb.append(windowFuncs[j]);
+        if(j < windowFuncs.length - 1) {
+          sb.append(",");
+        }
+      }
+      sb.append(")");
+      planStr.appendExplain("exprs: ").appendExplain(sb.toString());
+    }
+
+    sb = new StringBuilder("target list: ");
+    for (int i = 0; i < targets.length; i++) {
+      sb.append(targets[i]);
+      if( i < targets.length - 1) {
+        sb.append(", ");
+      }
+    }
+    planStr.addExplan(sb.toString());
+
+    planStr.addDetail("out schema:").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema:").appendDetail(getInSchema().toString());
+
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/b143f991/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowSpec.java
----------------------------------------------------------------------
diff --git a/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowSpec.java b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowSpec.java
new file mode 100644
index 0000000..73f4e13
--- /dev/null
+++ b/tajo-plan/src/main/java/org/apache/tajo/plan/logical/WindowSpec.java
@@ -0,0 +1,208 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.plan.logical;
+
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.plan.expr.EvalNode;
+import org.apache.tajo.util.TUtil;
+
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameEndBoundType;
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameStartBoundType;
+
+public class WindowSpec {
+  @Expose private String windowName;
+
+  @Expose private Column[] partitionKeys;
+
+  @Expose private WindowFrame windowFrame;
+
+  public String getWindowName() {
+    return windowName;
+  }
+
+  public boolean hasPartitionKeys() {
+    return partitionKeys != null;
+  }
+
+  public Column [] getPartitionKeys() {
+    return partitionKeys;
+  }
+
+  public boolean hasWindowFrame() {
+    return windowFrame != null;
+  }
+
+  public WindowFrame getWindowFrame() {
+    return windowFrame;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof WindowSpec) {
+      WindowSpec another = (WindowSpec) obj;
+      return
+          TUtil.checkEquals(windowName, another.windowName) &&
+          TUtil.checkEquals(partitionKeys, another.partitionKeys) &&
+          TUtil.checkEquals(windowFrame, another.windowFrame);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(windowName, partitionKeys, windowFrame);
+  }
+
+  public static class WindowFrame {
+    @Expose private WindowStartBound startBound;
+    @Expose private WindowEndBound endBound;
+    @Expose org.apache.tajo.algebra.WindowSpec.WindowFrameUnit unit; // TODO - to be supported
+
+    public WindowFrame() {
+      this.startBound = new WindowStartBound(WindowFrameStartBoundType.UNBOUNDED_PRECEDING);
+      this.endBound = new WindowEndBound(WindowFrameEndBoundType.UNBOUNDED_FOLLOWING);
+    }
+
+    public WindowFrame(WindowStartBound startBound) {
+      this.startBound = startBound;
+    }
+
+    public WindowFrame(WindowStartBound startBound, WindowEndBound endBound) {
+      this(startBound);
+      this.endBound = endBound;
+    }
+
+    public WindowStartBound getStartBound() {
+      return startBound;
+    }
+
+    public boolean hasEndBound() {
+      return endBound != null;
+    }
+
+    public WindowEndBound getEndBound() {
+      return endBound;
+    }
+
+    public boolean hasFrameUnit() {
+      return this.unit != null;
+    }
+
+    public void setFrameUnit(org.apache.tajo.algebra.WindowSpec.WindowFrameUnit unit) {
+      this.unit = unit;
+    }
+
+    public org.apache.tajo.algebra.WindowSpec.WindowFrameUnit getFrameUnit() {
+      return this.unit;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj instanceof WindowFrame) {
+        WindowFrame another = (WindowFrame) obj;
+        return
+            TUtil.checkEquals(startBound, another.startBound) &&
+            TUtil.checkEquals(endBound, another.endBound) &&
+            TUtil.checkEquals(unit, another.unit);
+      } else {
+        return false;
+      }
+    }
+
+    public int hashCode() {
+      return Objects.hashCode(startBound, endBound, unit);
+    }
+  }
+
+  public static class WindowStartBound {
+    @Expose private WindowFrameStartBoundType boundType;
+    @Expose private EvalNode number;
+
+    public WindowStartBound(WindowFrameStartBoundType type) {
+      this.boundType = type;
+    }
+
+    public WindowFrameStartBoundType getBoundType() {
+      return boundType;
+    }
+
+    public void setNumber(EvalNode number) {
+      this.number = number;
+    }
+
+    public EvalNode getNumber() {
+      return number;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj instanceof WindowStartBound) {
+        WindowStartBound other = (WindowStartBound) obj;
+        return boundType == other.boundType && number.equals(other.number);
+      } else {
+        return false;
+      }
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(boundType, number);
+    }
+  }
+
+  public static class WindowEndBound {
+    @Expose private WindowFrameEndBoundType boundType;
+    @Expose private EvalNode number;
+
+    public WindowEndBound(WindowFrameEndBoundType type) {
+      this.boundType = type;
+    }
+
+    public WindowFrameEndBoundType getBoundType() {
+      return boundType;
+    }
+
+    public EvalNode setNumber(EvalNode number) {
+      return number;
+    }
+
+    public EvalNode getNumber() {
+      return number;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      if (obj instanceof WindowStartBound) {
+        WindowEndBound other = (WindowEndBound) obj;
+        return boundType == other.boundType && number.equals(other.number);
+      } else {
+        return false;
+      }
+    }
+
+    @Override
+    public int hashCode() {
+      return Objects.hashCode(boundType, number);
+    }
+  }
+}


Mime
View raw message