You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tajo.apache.org by hy...@apache.org on 2014/04/18 13:44:37 UTC

[34/57] [abbrv] [partial] TAJO-752: Escalate sub modules in tajo-core into the top-level modules. (hyunsik)

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

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateDatabaseNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateDatabaseNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateDatabaseNode.java
new file mode 100644
index 0000000..9dc73e2
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateDatabaseNode.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.engine.planner.PlanString;
+
+public class CreateDatabaseNode extends LogicalNode implements Cloneable {
+  private String databaseName;
+  private boolean ifNotExists;
+
+  public CreateDatabaseNode(int pid) {
+    super(pid, NodeType.CREATE_DATABASE);
+  }
+
+  public void init(String databaseName, boolean ifNotExists) {
+    this.databaseName = databaseName;
+    this.ifNotExists = ifNotExists;
+  }
+
+  public String getDatabaseName() {
+    return this.databaseName;
+  }
+
+  public boolean isIfNotExists() {
+    return ifNotExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifNotExists ? " IF NOT EXISTS " : " ").appendTitle(databaseName);
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(databaseName, ifNotExists);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof CreateDatabaseNode) {
+      CreateDatabaseNode other = (CreateDatabaseNode) obj;
+      return super.equals(other) && this.databaseName.equals(other.databaseName) && ifNotExists == other.ifNotExists;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateDatabaseNode newNode = (CreateDatabaseNode) super.clone();
+    newNode.databaseName = databaseName;
+    newNode.ifNotExists = ifNotExists;
+    return newNode;
+  }
+
+  @Override
+  public String toString() {
+    return "CREATE DATABASE " + (ifNotExists ? " IF NOT EXISTS " : "")
+        + CatalogUtil.denormalizeIdentifier(databaseName);
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
new file mode 100644
index 0000000..c70fb10
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/CreateTableNode.java
@@ -0,0 +1,145 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.catalog.Options;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.util.TUtil;
+
+public class CreateTableNode extends StoreTableNode implements Cloneable {
+  @Expose private Schema schema;
+  @Expose private Path path;
+  @Expose private boolean external;
+  @Expose private boolean ifNotExists;
+
+  public CreateTableNode(int pid) {
+    super(pid, NodeType.CREATE_TABLE);
+  }
+
+  public void setTableSchema(Schema schema) {
+    this.schema = schema;
+  }
+    
+  public Schema getTableSchema() {
+    return this.schema;
+  }
+
+  public Schema getLogicalSchema() {
+    if (hasPartition()) {
+      Schema logicalSchema = new Schema(schema);
+      logicalSchema.addColumns(getPartitionMethod().getExpressionSchema());
+      return logicalSchema;
+    } else {
+      return schema;
+    }
+  }
+
+  public boolean hasPath() {
+    return this.path != null;
+  }
+
+  public void setPath(Path path) {
+    this.path = path;
+  }
+  
+  public Path getPath() {
+    return this.path;
+  }
+
+  public boolean isExternal() {
+    return external;
+  }
+
+  public void setExternal(boolean external) {
+    this.external = external;
+  }
+
+  public boolean hasSubQuery() {
+    return child != null;
+  }
+
+  public void setIfNotExists(boolean ifNotExists) {
+    this.ifNotExists = ifNotExists;
+  }
+
+  public boolean isIfNotExists() {
+    return ifNotExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this);
+  }
+
+  public int hashCode() {
+    return super.hashCode() ^ Objects.hashCode(schema, path, external, ifNotExists) * 31;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof CreateTableNode) {
+      CreateTableNode other = (CreateTableNode) obj;
+      return super.equals(other)
+          && this.schema.equals(other.schema)
+          && this.external == other.external
+          && TUtil.checkEquals(path, other.path)
+          && ifNotExists == other.ifNotExists;
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateTableNode createTableNode = (CreateTableNode) super.clone();
+    createTableNode.tableName = tableName;
+    createTableNode.schema = (Schema) schema.clone();
+    createTableNode.storageType = storageType;
+    createTableNode.external = external;
+    createTableNode.path = path != null ? new Path(path.toString()) : null;
+    createTableNode.options = (Options) (options != null ? options.clone() : null);
+    createTableNode.ifNotExists = ifNotExists;
+    return createTableNode;
+  }
+
+  public String toString() {
+    return "CreateTable (table=" + tableName + ", external=" + external + ", storeType=" + storageType +
+        ", ifNotExists=" + ifNotExists +")";
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    if (hasSubQuery()) {
+      child.preOrder(visitor);
+    }
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+    if (hasSubQuery()) {
+      child.preOrder(visitor);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropDatabaseNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropDatabaseNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropDatabaseNode.java
new file mode 100644
index 0000000..1578759
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropDatabaseNode.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.planner.PlanString;
+
+public class DropDatabaseNode extends LogicalNode implements Cloneable {
+  @Expose private String databaseName;
+  @Expose private boolean ifExists;
+
+  public DropDatabaseNode(int pid) {
+    super(pid, NodeType.DROP_DATABASE);
+  }
+
+  public void init(String databaseName, boolean ifExists) {
+    this.databaseName = databaseName;
+    this.ifExists = ifExists;
+  }
+
+  public String getDatabaseName() {
+    return this.databaseName;
+  }
+
+  public boolean isIfExists() {
+    return ifExists;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifExists ? " IF EXISTS " : " ").appendTitle(databaseName);
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(databaseName, ifExists);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DropDatabaseNode) {
+      DropDatabaseNode other = (DropDatabaseNode) obj;
+      return super.equals(other) && this.databaseName.equals(other.databaseName) && ifExists == other.ifExists;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DropDatabaseNode dropTableNode = (DropDatabaseNode) super.clone();
+    dropTableNode.databaseName = databaseName;
+    return dropTableNode;
+  }
+
+  @Override
+  public String toString() {
+    return "DROP DATABASE " + (ifExists ? "IF EXISTS ":"") + databaseName;
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
new file mode 100644
index 0000000..ac68a9c
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/DropTableNode.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.common.base.Objects;
+import org.apache.tajo.engine.planner.PlanString;
+
+public class DropTableNode extends LogicalNode implements Cloneable {
+  private String tableName;
+  private boolean ifExists;
+  private boolean purge;
+
+  public DropTableNode(int pid) {
+    super(pid, NodeType.DROP_TABLE);
+  }
+
+  public void init(String tableName, boolean ifExists, boolean purge) {
+    this.tableName = tableName;
+    this.ifExists = ifExists;
+    this.purge = purge;
+  }
+
+  public String getTableName() {
+    return this.tableName;
+  }
+
+  public boolean isIfExists() {
+    return this.ifExists;
+  }
+
+  public boolean isPurge() {
+    return this.purge;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(ifExists ? " IF EXISTS" : "").appendTitle(purge ? " PURGE" : "");
+  }
+
+  public int hashCode() {
+    return Objects.hashCode(tableName, ifExists, purge);
+  }
+
+  public boolean equals(Object obj) {
+    if (obj instanceof DropTableNode) {
+      DropTableNode other = (DropTableNode) obj;
+      return super.equals(other) &&
+          this.tableName.equals(other.tableName) &&
+          this.ifExists == other.ifExists &&
+          this.purge == other.purge;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DropTableNode dropTableNode = (DropTableNode) super.clone();
+    dropTableNode.tableName = tableName;
+    dropTableNode.ifExists = ifExists;
+    dropTableNode.purge = purge;
+    return dropTableNode;
+  }
+
+  @Override
+  public String toString() {
+    return "DROP TABLE " + (ifExists ? "IF EXISTS " : "") + tableName + (purge ? " PURGE" : "");
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
new file mode 100644
index 0000000..6ea3f40
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/EvalExprNode.java
@@ -0,0 +1,83 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.Target;
+import org.apache.tajo.util.TUtil;
+
+public class EvalExprNode extends LogicalNode implements Projectable {
+  @Expose private Target[] exprs;
+
+  public EvalExprNode(int pid) {
+    super(pid, NodeType.EXPRS);
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return true;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.exprs = targets;
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return exprs;
+  }
+
+  public Target[] getExprs() {
+    return this.exprs;
+  }
+  
+  @Override
+  public String toString() {
+    return "EvalExprNode (" + TUtil.arrayToString(exprs) + ")";
+  }
+
+  public boolean equals(Object object) {
+    if (object instanceof EvalExprNode) {
+      EvalExprNode other = (EvalExprNode) object;
+      return TUtil.checkEquals(this.exprs, other.exprs);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    // nothing
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    // nothing
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return null;
+  }
+}

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

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
new file mode 100644
index 0000000..bafe0c6
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/GroupbyNode.java
@@ -0,0 +1,198 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.engine.eval.AggregationFunctionCallEval;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
+import org.apache.tajo.engine.planner.Target;
+import org.apache.tajo.util.TUtil;
+
+public class GroupbyNode extends UnaryNode implements Projectable, Cloneable {
+	/** Grouping key sets */
+  @Expose private Column [] groupingColumns;
+  /** Aggregation Functions */
+  @Expose private AggregationFunctionCallEval [] aggrFunctions;
+  /**
+   * It's a list of targets. The grouping columns should be followed by aggregation functions.
+   * aggrFunctions keep actual aggregation functions, but it only contains field references.
+   * */
+  @Expose private Target [] targets;
+  @Expose private boolean hasDistinct = false;
+
+  public GroupbyNode(int pid) {
+    super(pid, NodeType.GROUP_BY);
+  }
+
+  public final boolean isEmptyGrouping() {
+    return groupingColumns == null || groupingColumns.length == 0;
+  }
+
+  public void setGroupingColumns(Column [] groupingColumns) {
+    this.groupingColumns = groupingColumns;
+  }
+
+	public final Column [] getGroupingColumns() {
+	  return this.groupingColumns;
+	}
+
+  public final boolean isDistinct() {
+    return hasDistinct;
+  }
+
+  public void setDistinct(boolean distinct) {
+    hasDistinct = distinct;
+  }
+
+  public boolean hasAggFunctions() {
+    return this.aggrFunctions != null;
+  }
+
+  public AggregationFunctionCallEval [] getAggFunctions() {
+    return this.aggrFunctions;
+  }
+
+  public void setAggFunctions(AggregationFunctionCallEval[] evals) {
+    this.aggrFunctions = evals;
+  }
+
+  @Override
+  public boolean hasTargets() {
+    return this.targets != null;
+  }
+
+  @Override
+  public void setTargets(Target[] targets) {
+    this.targets = targets;
+    setOutSchema(PlannerUtil.targetToSchema(targets));
+  }
+
+  @Override
+  public Target[] getTargets() {
+    return this.targets;
+  }
+  
+  public void setChild(LogicalNode subNode) {
+    super.setChild(subNode);
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder("GroupBy (");
+    if (groupingColumns != null || groupingColumns.length > 0) {
+      sb.append("grouping set=").append(TUtil.arrayToString(groupingColumns));
+      sb.append(", ");
+    }
+    if (hasAggFunctions()) {
+      sb.append("funcs=").append(TUtil.arrayToString(aggrFunctions));
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof GroupbyNode) {
+      GroupbyNode other = (GroupbyNode) obj;
+      boolean eq = super.equals(other);
+      eq = eq && TUtil.checkEquals(groupingColumns, other.groupingColumns);
+      eq = eq && TUtil.checkEquals(aggrFunctions, other.aggrFunctions);
+      eq = eq && TUtil.checkEquals(targets, other.targets);
+      return eq;
+    } else {
+      return false;  
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    GroupbyNode grp = (GroupbyNode) super.clone();
+    if (groupingColumns != null) {
+      grp.groupingColumns = new Column[groupingColumns.length];
+      for (int i = 0; i < groupingColumns.length; i++) {
+        grp.groupingColumns[i] = groupingColumns[i];
+      }
+    }
+
+    if (aggrFunctions != null) {
+      grp.aggrFunctions = new AggregationFunctionCallEval[aggrFunctions.length];
+      for (int i = 0; i < aggrFunctions.length; i++) {
+        grp.aggrFunctions[i] = (AggregationFunctionCallEval) aggrFunctions[i].clone();
+      }
+    }
+
+    if (targets != null) {
+      grp.targets = new Target[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        grp.targets[i] = (Target) targets[i].clone();
+      }
+    }
+
+    return grp;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planStr = new PlanString(this);
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("(");
+    Column [] groupingColumns = this.groupingColumns;
+    for (int j = 0; j < groupingColumns.length; j++) {
+      sb.append(groupingColumns[j].getSimpleName());
+      if(j < groupingColumns.length - 1) {
+        sb.append(",");
+      }
+    }
+
+    sb.append(")");
+
+    planStr.appendTitle(sb.toString());
+
+    // there can be no aggregation functions
+    if (hasAggFunctions()) {
+      sb = new StringBuilder();
+      sb.append("(");
+
+      for (int j = 0; j < aggrFunctions.length; j++) {
+        sb.append(aggrFunctions[j]);
+        if(j < aggrFunctions.length - 1) {
+          sb.append(",");
+        }
+      }
+      sb.append(")");
+      planStr.appendExplain("exprs: ").appendExplain(sb.toString());
+    }
+
+    sb = new StringBuilder("target list: ");
+    for (int i = 0; i < targets.length; i++) {
+      sb.append(targets[i]);
+      if( i < targets.length - 1) {
+        sb.append(", ");
+      }
+    }
+    planStr.addExplan(sb.toString());
+
+    planStr.addDetail("out schema:").appendDetail(getOutSchema().toString());
+    planStr.addDetail("in schema:").appendDetail(getInSchema().toString());
+
+    return planStr;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.java
new file mode 100644
index 0000000..6c45868
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/HavingNode.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.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.eval.EvalNode;
+import org.apache.tajo.engine.planner.PlanString;
+
+public class HavingNode extends UnaryNode implements Cloneable {
+	@Expose private EvalNode qual;
+
+  public HavingNode(int pid) {
+    super(pid, NodeType.HAVING);
+  }
+
+	public EvalNode getQual() {
+		return this.qual;
+	}
+
+	public void setQual(EvalNode qual) {
+		this.qual = qual;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof HavingNode) {
+      HavingNode other = (HavingNode) obj;
+      return super.equals(other) 
+          && this.qual.equals(other.qual);
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    HavingNode selNode = (HavingNode) super.clone();
+    selNode.qual = (EvalNode) this.qual.clone();
+    
+    return selNode;
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    return new PlanString(this).appendTitle(" (").appendTitle(qual.toString()).appendTitle(")");
+  }
+
+  public String toString() {
+    return "Having (filter=" + qual + ")";
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
new file mode 100644
index 0000000..bff0b31
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/IndexScanNode.java
@@ -0,0 +1,122 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.engine.json.CoreGsonHelper;
+
+public class IndexScanNode extends ScanNode {
+  @Expose private SortSpec [] sortKeys;
+  @Expose private Schema keySchema = null;
+  @Expose private Datum[] datum = null;
+  
+  public IndexScanNode(int pid, ScanNode scanNode ,
+      Schema keySchema , Datum[] datum, SortSpec[] sortKeys ) {
+    super(pid);
+    init(scanNode.getTableDesc());
+    setQual(scanNode.getQual());
+    setInSchema(scanNode.getInSchema());
+    setTargets(scanNode.getTargets());
+    setType(NodeType.BST_INDEX_SCAN);
+    this.sortKeys = sortKeys;
+    this.keySchema = keySchema;
+    this.datum = datum;
+  }
+  
+  public SortSpec[] getSortKeys() {
+    return this.sortKeys;
+  }
+  
+  public Schema getKeySchema() {
+    return this.keySchema;
+  }
+  
+  public Datum[] getDatum() {
+    return this.datum;
+  }
+  
+  public void setSortKeys(SortSpec[] sortKeys) {
+    this.sortKeys = sortKeys;
+  }
+  
+  public void setKeySchema( Schema keySchema ) {
+    this.keySchema = keySchema;
+  }
+
+  @Override
+  public String toString() {
+    Gson gson = CoreGsonHelper.getInstance();
+    StringBuilder builder = new StringBuilder();
+    builder.append("IndexScanNode : {\n");
+    builder.append("  \"keySchema\" : \"" + gson.toJson(this.keySchema) + "\"\n");
+    builder.append("  \"sortKeys\" : \"" + gson.toJson(this.sortKeys) + " \"\n");
+    builder.append("  \"datums\" : \"" + gson.toJson(this.datum) + "\"\n");
+    builder.append("      <<\"superClass\" : " + super.toString());
+    builder.append(">>}");
+    builder.append("}");
+    return builder.toString();
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof IndexScanNode) {
+      IndexScanNode other = (IndexScanNode) obj;
+      
+      boolean eq = super.equals(other);
+      eq = eq && this.sortKeys.length == other.sortKeys.length;
+      if(eq) {
+        for(int i = 0 ; i < this.sortKeys.length ; i ++) {
+          eq = eq && this.sortKeys[i].getSortKey().equals(
+              other.sortKeys[i].getSortKey());
+          eq = eq && this.sortKeys[i].isAscending()
+              == other.sortKeys[i].isAscending();
+          eq = eq && this.sortKeys[i].isNullFirst()
+              == other.sortKeys[i].isNullFirst();
+        }
+      }
+      if(eq) {
+        for(int i = 0 ; i < this.datum.length ; i ++ ) {
+          eq = eq && this.datum[i].equals(other.datum[i]);
+        }
+      }
+     return eq;
+    }   
+    return false;
+  } 
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    IndexScanNode indexNode = (IndexScanNode) super.clone();
+    indexNode.keySchema = (Schema) this.keySchema.clone();
+    indexNode.sortKeys = new SortSpec[this.sortKeys.length];
+    for(int i = 0 ; i < sortKeys.length ; i ++ )
+      indexNode.sortKeys[i] = (SortSpec) this.sortKeys[i].clone();
+    indexNode.datum = new Datum[this.datum.length];
+    for(int i = 0 ; i < datum.length ; i ++ ) {
+      indexNode.datum[i] = this.datum[i];
+    }
+    return indexNode;
+  }
+}
+
+

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/InsertNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/InsertNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/InsertNode.java
new file mode 100644
index 0000000..f5e87ef
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/InsertNode.java
@@ -0,0 +1,182 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.hadoop.fs.Path;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.TableDesc;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.util.TUtil;
+
+public class InsertNode extends StoreTableNode implements Cloneable {
+  @Expose private boolean overwrite;
+  @Expose private Schema tableSchema;
+
+  /** a target schema of a target table */
+  @Expose private Schema targetSchema;
+  /** a output schema of select clause */
+  @Expose private Schema projectedSchema;
+  @Expose private Path path;
+
+  public InsertNode(int pid) {
+    super(pid, NodeType.INSERT);
+  }
+
+  public void setTargetTable(TableDesc desc) {
+    setTableName(desc.getName());
+    if (desc.hasPartition()) {
+      tableSchema = desc.getLogicalSchema();
+    } else {
+      tableSchema = desc.getSchema();
+    }
+    setPath(desc.getPath());
+    setOptions(desc.getMeta().getOptions());
+    setStorageType(desc.getMeta().getStoreType());
+
+    if (desc.hasPartition()) {
+      this.setPartitionMethod(desc.getPartitionMethod());
+    }
+  }
+
+  public void setTargetLocation(Path path) {
+    this.path = path;
+  }
+
+  public void setSubQuery(LogicalNode subQuery) {
+    this.setChild(subQuery);
+    this.setInSchema(subQuery.getOutSchema());
+    this.setOutSchema(subQuery.getOutSchema());
+  }
+
+  public boolean isOverwrite() {
+    return overwrite;
+  }
+
+  public void setOverwrite(boolean overwrite) {
+    this.overwrite = overwrite;
+  }
+
+  public Schema getTableSchema() {
+    return tableSchema;
+  }
+
+  public void setTableSchema(Schema tableSchema) {
+    this.tableSchema = tableSchema;
+  }
+
+  public boolean hasTargetSchema() {
+    return this.targetSchema != null;
+  }
+
+  public Schema getTargetSchema() {
+    return this.targetSchema;
+  }
+
+  public void setTargetSchema(Schema schema) {
+    this.targetSchema = schema;
+  }
+
+  public Schema getProjectedSchema() {
+    return this.projectedSchema;
+  }
+
+  public void setProjectedSchema(Schema projected) {
+    this.projectedSchema = projected;
+  }
+
+  public boolean hasPath() {
+    return this.path != null;
+  }
+
+  public void setPath(Path path) {
+    this.path = path;
+  }
+  
+  public Path getPath() {
+    return this.path;
+  }
+
+  public boolean hasStorageType() {
+    return this.storageType != null;
+  }
+  
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof InsertNode) {
+      InsertNode other = (InsertNode) obj;
+      return super.equals(other)
+          && this.overwrite == other.overwrite
+          && TUtil.checkEquals(this.tableSchema, other.tableSchema)
+          && TUtil.checkEquals(this.targetSchema, other.targetSchema)
+          && TUtil.checkEquals(path, other.path);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    InsertNode insertNode = (InsertNode) super.clone();
+    insertNode.overwrite = overwrite;
+    insertNode.tableSchema = new Schema(tableSchema);
+    insertNode.targetSchema = targetSchema != null ? new Schema(targetSchema) : null;
+    insertNode.path = path != null ? new Path(path.toString()) : null;
+    return insertNode;
+  }
+  
+  public String toString() {
+    StringBuilder sb = new StringBuilder("Insert (overwrite=").append(overwrite);
+    if (hasTargetTable()) {
+      sb.append(",table=").append(tableName);
+    }
+    if (hasPath()) {
+      sb.append(", location=").append(path);
+    }
+    sb.append(")");
+    return sb.toString();
+  }
+
+  @Override
+  public void preOrder(LogicalNodeVisitor visitor) {
+    getChild().preOrder(visitor);
+    visitor.visit(this);
+  }
+
+  @Override
+  public void postOrder(LogicalNodeVisitor visitor) {
+    visitor.visit(this);
+    getChild().postOrder(visitor);
+  }
+
+  @Override
+  public PlanString getPlanString() {
+    PlanString planString = new PlanString(this);
+    planString.appendTitle(" INTO ");
+    if (hasTargetTable()) {
+      planString.appendTitle(getTableName());
+      if (hasTargetSchema()) {
+        planString.addExplan(getTargetSchema().toString());
+      }
+    } else {
+      planString.addExplan("LOCATION " + path);
+    }
+    return planString;
+  }
+}
\ No newline at end of file

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

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
new file mode 100644
index 0000000..915c66d
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/JoinNode.java
@@ -0,0 +1,165 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * 
+ */
+package org.apache.tajo.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.algebra.JoinType;
+import org.apache.tajo.engine.eval.EvalNode;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
+import org.apache.tajo.engine.planner.Target;
+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;
+
+  @Expose private boolean candidateBroadcast = false;
+  @Expose private List<LogicalNode> broadcastTargets = 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> getBroadcastTargets() {
+    return broadcastTargets;
+  }
+
+  public void setBroadcastTargets(List<LogicalNode> broadcastTargets) {
+    this.broadcastTargets = broadcastTargets;
+  }
+
+  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 : (EvalNode) 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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LimitNode.java
new file mode 100644
index 0000000..7f5b258
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.planner.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNode.java
new file mode 100644
index 0000000..f62caa4
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.engine.json.CoreGsonHelper;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
+import org.apache.tajo.json.GsonObject;
+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 CoreGsonHelper.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNodeVisitor.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNodeVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalNodeVisitor.java
new file mode 100644
index 0000000..5b0c1c2
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+
+public interface LogicalNodeVisitor {
+  void visit(LogicalNode node);
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalRootNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalRootNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/LogicalRootNode.java
new file mode 100644
index 0000000..bacf14f
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+import org.apache.tajo.engine.planner.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
new file mode 100644
index 0000000..f498231
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/NodeType.java
@@ -0,0 +1,66 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ *
+ */
+package org.apache.tajo.engine.planner.logical;
+
+
+import org.apache.tajo.engine.planner.AlterTablespaceNode;
+
+/**
+ * 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),
+  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),
+
+  CREATE_DATABASE(CreateDatabaseNode.class),
+  DROP_DATABASE(DropDatabaseNode.class),
+  CREATE_TABLE(CreateTableNode.class),
+  DROP_TABLE(DropTableNode.class),
+  ALTER_TABLESPACE (AlterTablespaceNode.class),
+  ALTER_TABLE (AlterTableNode.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PartitionedTableScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PartitionedTableScanNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PartitionedTableScanNode.java
new file mode 100644
index 0000000..45cc578
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PartitionedTableScanNode.java
@@ -0,0 +1,155 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.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.engine.eval.EvalNode;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.Target;
+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;
+  }
+
+  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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PersistentStoreNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PersistentStoreNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/PersistentStoreNode.java
new file mode 100644
index 0000000..9d2acf6
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Options;
+import org.apache.tajo.engine.planner.PlanString;
+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 Options options;
+
+  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 Options getOptions() {
+    return this.options;
+  }
+
+  public void setOptions(Options 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 ? (Options) options.clone() : null;
+    return store;
+  }
+}

http://git-wip-us.apache.org/repos/asf/tajo/blob/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/Projectable.java
new file mode 100644
index 0000000..1e4bdc5
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.engine.planner.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 org.apache.tajo.engine.planner.logical.ScanNode},
+ * {@link org.apache.tajo.engine.planner.logical.JoinNode},
+ * {@link org.apache.tajo.engine.planner.logical.GroupbyNode}, and
+ * {@link org.apache.tajo.engine.planner.logical.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ProjectionNode.java
new file mode 100644
index 0000000..e9fd803
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
+import org.apache.tajo.engine.planner.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/RelationNode.java
new file mode 100644
index 0000000..83c16cd
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.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/6594ac1c/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
----------------------------------------------------------------------
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
new file mode 100644
index 0000000..27782a2
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/ScanNode.java
@@ -0,0 +1,234 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.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.engine.eval.EvalNode;
+import org.apache.tajo.engine.planner.PlanString;
+import org.apache.tajo.engine.planner.PlannerUtil;
+import org.apache.tajo.engine.planner.Target;
+import org.apache.tajo.engine.utils.SchemaUtil;
+import org.apache.tajo.util.TUtil;
+
+public class ScanNode extends RelationNode implements Projectable, 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();
+    }
+  }
+
+  public Schema getTableSchema() {
+    return logicalSchema;
+  }
+
+  public Schema getPhysicalSchema() {
+    return getInSchema();
+  }
+	
+	public boolean hasQual() {
+	  return qual != null;
+	}
+	
+	public EvalNode getQual() {
+	  return this.qual;
+	}
+	
+	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();
+      }
+	  }
+	  
+	  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;
+  }
+}