TAJO-774: Implement logical plan part and physical executor for window function.

Closes #13
diff --git a/CHANGES b/CHANGES
index 3dd67a9..055b3c9 100644
--- a/CHANGES
+++ b/CHANGES
@@ -4,6 +4,9 @@
 
   NEW FEATURES 
 
+    TAJO-774: Implement logical plan part and physical executor for window 
+    function. (hyunsik)
+
     TAJO-847: Supporting MariaDB-based Store, which is compatible with MySQL.
     (Jinhang Choi via jihoon)
 
@@ -68,10 +71,6 @@
     TAJO-789: Improve shuffle URI. (jinho)
 
     TAJO-769: A minor improvements for HCatalogStore (Fengdong Yu via hyunsik)
-    
-  SUB TASKS:
-
-    TAJO-757: Add parser rule and algebraic expression for window function. (hyunsik)
 
     TAJO-734: Arrange TajoCli output message. (hyoungjunkim via jihoon)
 
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Aggregation.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Aggregation.java
index bf95e89..edb523b 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Aggregation.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Aggregation.java
@@ -67,7 +67,23 @@
     return a && b;
   }
 
-  public static class GroupElement implements JsonSerializable {
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Aggregation aggregation = (Aggregation) super.clone();
+
+    aggregation.namedExprs = new NamedExpr[namedExprs.length];
+    for (int i = 0; i < namedExprs.length; i++) {
+      aggregation.namedExprs[i] = (NamedExpr) namedExprs[i].clone();
+    }
+
+    aggregation.groups = new GroupElement[groups.length];
+    for (int i = 0; i < groups.length; i++) {
+      aggregation.groups[i] = (GroupElement) groups[i].clone();
+    }
+    return aggregation;
+  }
+
+  public static class GroupElement implements JsonSerializable, Cloneable {
     @Expose @SerializedName("GroupType")
     private GroupType group_type;
     @Expose @SerializedName("Dimensions")
@@ -110,6 +126,19 @@
 
       return false;
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      GroupElement element = (GroupElement) super.clone();
+      element.group_type = group_type;
+      if (element.grouping_sets != null) {
+        element.grouping_sets = new Expr[grouping_sets.length];
+        for (int i = 0; i < grouping_sets.length; i++) {
+          element.grouping_sets[i] = (Expr) grouping_sets[i].clone();
+        }
+      }
+      return element;
+    }
   }
 
   public static enum GroupType {
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTable.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTable.java
index 0f56bc2..6d72472 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTable.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTable.java
@@ -115,5 +115,15 @@
         TUtil.checkEquals(alterTableOpType, another.alterTableOpType);
   }
 
-
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    AlterTable alter = (AlterTable) super.clone();
+    alter.tableName = tableName;
+    alter.newTableName = newTableName;
+    alter.columnName = columnName;
+    alter.newColumnName = newColumnName;
+    alter.addNewColumn = (ColumnDefinition) addNewColumn.clone();
+    alter.alterTableOpType = alterTableOpType;
+    return alter;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTablespace.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTablespace.java
index b9cb0c7..b17b53e 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTablespace.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/AlterTablespace.java
@@ -68,4 +68,13 @@
         TUtil.checkEquals(setType, another.setType) &&
         TUtil.checkEquals(uri, another.uri);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    AlterTablespace alter = (AlterTablespace) super.clone();
+    alter.setType = setType;
+    alter.tablespaceName = tablespaceName;
+    alter.uri = uri;
+    return alter;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
index 4d4f09e..4b17bdb 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BetweenPredicate.java
@@ -75,4 +75,14 @@
     return symmetric == another.symmetric && predicand.equals(another.predicand) && begin.equals(another.begin) &&
         end.equals(another.end);
   }
+
+  public Object clone() throws CloneNotSupportedException {
+    BetweenPredicate between = (BetweenPredicate) super.clone();
+    between.not = not;
+    between.symmetric = symmetric;
+    between.predicand = (Expr) predicand.clone();
+    between.begin = (Expr) between.clone();
+    between.end = (Expr) end.clone();
+    return between;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/BinaryOperator.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BinaryOperator.java
index 0cc6376..4ca3788 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/BinaryOperator.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/BinaryOperator.java
@@ -67,4 +67,12 @@
   public String toString() {
     return left.toString() + " " + opType.toString() + " " + right.toString();
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    BinaryOperator binaryOperator = (BinaryOperator) super.clone();
+    binaryOperator.left = (Expr) left.clone();
+    binaryOperator.right = (Expr) right.clone();
+    return binaryOperator;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CaseWhenPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CaseWhenPredicate.java
index 41eba6e..8e97c31 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CaseWhenPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CaseWhenPredicate.java
@@ -69,11 +69,22 @@
   }
 
   @Override
+  public Object clone() throws CloneNotSupportedException {
+    CaseWhenPredicate caseWhen = (CaseWhenPredicate) super.clone();
+    caseWhen.whens = new ArrayList<WhenExpr>();
+    for (int i = 0; i < whens.size(); i++) {
+      caseWhen.whens.add((WhenExpr) whens.get(i).clone());
+    }
+    caseWhen.elseResult = elseResult != null ? (Expr) elseResult.clone() : null;
+    return caseWhen;
+  }
+
+  @Override
   public String toJson() {
     return JsonHelper.toJson(this);
   }
 
-  public static class WhenExpr {
+  public static class WhenExpr implements Cloneable {
     @Expose @SerializedName("Condition")
     Expr condition;
     @Expose @SerializedName("Result")
@@ -112,5 +123,13 @@
 
       return false;
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      WhenExpr when = (WhenExpr) super.clone();
+      when.condition = (Expr) condition.clone();
+      when.result = (Expr) result.clone();
+      return when;
+    }
   }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnDefinition.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnDefinition.java
index c20d409..e6e05d4 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnDefinition.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnDefinition.java
@@ -22,11 +22,11 @@
 
 public  class ColumnDefinition extends DataTypeExpr {
   @Expose @SerializedName("ColumnDefName")
-  String col_name;
+  String columnName;
 
   public ColumnDefinition(String columnName, String dataType) {
     super(dataType);
-    this.col_name = columnName;
+    this.columnName = columnName;
   }
 
   public ColumnDefinition(String columnName, DataTypeExpr dataType) {
@@ -37,17 +37,17 @@
         setScale(dataType.scale);
       }
     }
-    this.col_name = columnName;
+    this.columnName = columnName;
   }
 
   public String getColumnName() {
-    return this.col_name;
+    return this.columnName;
   }
 
   @Override
   public int hashCode() {
     int hash = super.hashCode();
-    return hash * 89 * col_name.hashCode();
+    return hash * 89 * columnName.hashCode();
 
   }
 
@@ -55,9 +55,16 @@
   public boolean equalsTo(Expr expr) {
     if (expr instanceof ColumnDefinition) {
       ColumnDefinition another = (ColumnDefinition) expr;
-      return col_name.equals(another.col_name) && super.equalsTo(another);
+      return columnName.equals(another.columnName) && super.equalsTo(another);
     }
 
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    ColumnDefinition column = (ColumnDefinition) super.clone();
+    column.columnName = columnName;
+    return column;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnReferenceExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnReferenceExpr.java
index 97bf332..456b570 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnReferenceExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ColumnReferenceExpr.java
@@ -91,4 +91,12 @@
   public String toString() {
     return qualifier != null ? qualifier + "." + name : name;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    ColumnReferenceExpr column = (ColumnReferenceExpr) super.clone();
+    column.qualifier = qualifier;
+    column.name = name;
+    return column;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateDatabase.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateDatabase.java
index 2ff0ce7..1144b6e 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateDatabase.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateDatabase.java
@@ -83,4 +83,13 @@
         ifNotExists == another.ifNotExists;
 
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateDatabase createDb = (CreateDatabase) super.clone();
+    createDb.databaseName = databaseName;
+    createDb.tablespaceName = tablespaceName;
+    createDb.ifNotExists = ifNotExists;
+    return createDb;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateTable.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateTable.java
index c74677d..bd04a91 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateTable.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/CreateTable.java
@@ -24,6 +24,8 @@
 import com.google.gson.annotations.SerializedName;
 import org.apache.tajo.util.TUtil;
 
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
@@ -179,6 +181,26 @@
         ifNotExists == another.ifNotExists;
   }
 
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    CreateTable createTable = (CreateTable) super.clone();
+    createTable.external = external;
+    createTable.tableName = tableName;
+    if (tableElements != null) {
+      createTable.tableElements = new ColumnDefinition[tableElements.length];
+      for (int i = 0; i < tableElements.length; i++) {
+        createTable.tableElements[i] = (ColumnDefinition) tableElements[i].clone();
+      }
+    }
+    createTable.storageType = storageType;
+    createTable.location = location;
+    createTable.subquery = subquery;
+    createTable.params = new HashMap<String, String>(params);
+    createTable.partition = (PartitionMethodDescExpr) partition.clone();
+    createTable.ifNotExists = ifNotExists;
+    return createTable;
+  }
+
   public static enum PartitionType {
     RANGE,
     HASH,
@@ -186,7 +208,7 @@
     COLUMN
   }
 
-  public static abstract class PartitionMethodDescExpr {
+  public static abstract class PartitionMethodDescExpr implements Cloneable {
     @Expose @SerializedName("PartitionType")
     PartitionType type;
 
@@ -198,6 +220,13 @@
       return type;
     }
 
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      PartitionMethodDescExpr partition = (PartitionMethodDescExpr) super.clone();
+      partition.type = type;
+      return partition;
+    }
+
     static class JsonSerDer implements JsonSerializer<PartitionMethodDescExpr>,
         JsonDeserializer<PartitionMethodDescExpr> {
 
@@ -237,7 +266,7 @@
     }
   }
 
-  public static class RangePartition extends PartitionMethodDescExpr {
+  public static class RangePartition extends PartitionMethodDescExpr implements Cloneable {
     @Expose @SerializedName("Columns")
     ColumnReferenceExpr [] columns;
     @Expose @SerializedName("Specifiers")
@@ -270,9 +299,25 @@
         return false;
       }
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      RangePartition range = (RangePartition) super.clone();
+      range.columns = new ColumnReferenceExpr[columns.length];
+      for (int i = 0; i < columns.length; i++) {
+        range.columns[i] = (ColumnReferenceExpr) columns[i].clone();
+      }
+      if (range.specifiers != null) {
+        range.specifiers = new ArrayList<RangePartitionSpecifier>();
+        for (int i = 0; i < specifiers.size(); i++) {
+          range.specifiers.add(specifiers.get(i));
+        }
+      }
+      return range;
+    }
   }
 
-  public static class HashPartition extends PartitionMethodDescExpr {
+  public static class HashPartition extends PartitionMethodDescExpr implements Cloneable {
     @Expose @SerializedName("Columns")
     ColumnReferenceExpr [] columns;
     @Expose @SerializedName("Quantity")
@@ -325,6 +370,23 @@
         return false;
       }
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      HashPartition hash = (HashPartition) super.clone();
+      hash.columns = new ColumnReferenceExpr[columns.length];
+      for (int i = 0; i < columns.length; i++) {
+        hash.columns[i] = (ColumnReferenceExpr) columns[i].clone();
+      }
+      hash.quantity = quantity;
+      if (specifiers != null) {
+        hash.specifiers = new ArrayList<PartitionSpecifier>();
+        for (PartitionSpecifier specifier : specifiers) {
+          hash.specifiers.add(specifier);
+        }
+      }
+      return hash;
+    }
   }
 
   public static class ListPartition extends PartitionMethodDescExpr {
@@ -360,6 +422,22 @@
         return false;
       }
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      ListPartition listPartition = (ListPartition) super.clone();
+      listPartition.columns = new ColumnReferenceExpr[columns.length];
+      for (int i = 0; i < columns.length; i++) {
+        listPartition.columns[i] = (ColumnReferenceExpr) columns[i].clone();
+      }
+      if (specifiers != null) {
+        listPartition.specifiers = new ArrayList<ListPartitionSpecifier>();
+        for (ListPartitionSpecifier specifier : specifiers) {
+          listPartition.specifiers.add(specifier);
+        }
+      }
+      return listPartition;
+    }
   }
 
   public static class ColumnPartition extends PartitionMethodDescExpr {
@@ -368,35 +446,39 @@
     @Expose @SerializedName("IsOmitValues")
     private boolean isOmitValues;
 
-    public ColumnPartition(ColumnDefinition [] columns, boolean isOmitValues) {
+    public ColumnPartition(ColumnDefinition [] columns) {
       super(PartitionType.COLUMN);
       this.columns = columns;
-      this.isOmitValues = isOmitValues;
     }
 
     public ColumnDefinition [] getColumns() {
       return columns;
     }
 
-    public boolean isOmitValues() {
-      return isOmitValues;
-    }
-
     public int hashCode() {
-      return Objects.hashCode(Objects.hashCode(columns), isOmitValues);
+      return Objects.hashCode(columns);
     }
 
     public boolean equals(Object object) {
       if (object instanceof ColumnPartition) {
         ColumnPartition another = (ColumnPartition) object;
-        return type == another.type && TUtil.checkEquals(columns, another.columns) &&
-            TUtil.checkEquals(isOmitValues, another.isOmitValues);
+        return type == another.type && TUtil.checkEquals(columns, another.columns);
       }
       return false;
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      ColumnPartition columnPartition = (ColumnPartition) super.clone();
+      columnPartition.columns = new ColumnDefinition[columns.length];
+      for (int i = 0; i < columns.length; i++) {
+        columnPartition.columns[i] = (ColumnDefinition) columns[i].clone();
+      }
+      return columnPartition;
+    }
   }
 
-  public static class RangePartitionSpecifier extends PartitionSpecifier {
+  public static class RangePartitionSpecifier extends PartitionSpecifier implements Cloneable {
     @Expose @SerializedName("End")
     Expr end;
     @Expose @SerializedName("IsMaxValue")
@@ -436,9 +518,17 @@
 
       return true;
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      RangePartitionSpecifier specifier = (RangePartitionSpecifier) super.clone();
+      specifier.end = (Expr) end.clone();
+      specifier.maxValue = maxValue;
+      return specifier;
+    }
   }
 
-  public static class ListPartitionSpecifier extends PartitionSpecifier {
+  public static class ListPartitionSpecifier extends PartitionSpecifier implements Cloneable {
     @Expose @SerializedName("ValueList")
     ValueListExpr valueList;
 
@@ -465,9 +555,16 @@
 
       return valueList.equals(that.valueList);
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      ListPartitionSpecifier specifier = (ListPartitionSpecifier) super.clone();
+      specifier.valueList = (ValueListExpr) valueList.clone();
+      return specifier;
+    }
   }
 
-  public static class PartitionSpecifier {
+  public static class PartitionSpecifier implements Cloneable {
     @Expose @SerializedName("PartitionSpecName")
     private String name;
 
@@ -490,5 +587,12 @@
         return false;
       }
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      PartitionSpecifier specifier = (PartitionSpecifier) super.clone();
+      specifier.name = name;
+      return specifier;
+    }
   }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DataTypeExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DataTypeExpr.java
index 6978b84..9dc795b 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DataTypeExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DataTypeExpr.java
@@ -76,4 +76,13 @@
         TUtil.checkEquals(lengthOrPrecision, another.lengthOrPrecision) &&
         TUtil.checkEquals(scale, another.scale);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DataTypeExpr dataType = (DataTypeExpr) super.clone();
+    dataType.typeName = typeName;
+    dataType.lengthOrPrecision = lengthOrPrecision;
+    dataType.scale = scale;
+    return dataType;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateLiteral.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateLiteral.java
index fafa8f8..297a44e 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateLiteral.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateLiteral.java
@@ -51,4 +51,11 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DateLiteral newDate = (DateLiteral) super.clone();
+    newDate.date = date;
+    return newDate;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateValue.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateValue.java
index 8424ab4..5878f92 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateValue.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DateValue.java
@@ -22,7 +22,7 @@
 import com.google.gson.annotations.Expose;
 import com.google.gson.annotations.SerializedName;
 
-public class DateValue {
+public class DateValue implements Cloneable {
   @Expose @SerializedName("Year")
   private String years;
   @Expose @SerializedName("Month")
@@ -65,4 +65,13 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DateValue date = (DateValue) super.clone();
+    date.years = years;
+    date.months = months;
+    date.days = days;
+    return date;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropDatabase.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropDatabase.java
index fc949fd..e66b59d 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropDatabase.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropDatabase.java
@@ -52,4 +52,12 @@
     DropDatabase another = (DropDatabase) expr;
     return databaseName.equals(another.databaseName) && ifExists == another.ifExists;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    DropDatabase drop = (DropDatabase) super.clone();
+    drop.databaseName = databaseName;
+    drop.ifExists = ifExists;
+    return drop;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropTable.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropTable.java
index c6e69dc..2c55b7f 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropTable.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/DropTable.java
@@ -24,11 +24,11 @@
 
 public class DropTable extends Expr {
   @Expose @SerializedName("TableName")
-  private final String tableName;
+  private String tableName;
   @Expose @SerializedName("IfExists")
-  private final boolean ifExists;
+  private boolean ifExists;
   @Expose @SerializedName("IsPurge")
-  private final boolean purge;
+  private boolean purge;
 
   public DropTable(String tableName, boolean ifExists, boolean purge) {
     super(OpType.DropTable);
@@ -64,4 +64,12 @@
     }
     return false;
   }
+
+  public Object clone() throws CloneNotSupportedException {
+    DropTable drop = (DropTable) super.clone();
+    drop.tableName = tableName;
+    drop.ifExists = ifExists;
+    drop.purge = purge;
+    return drop;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
index 6bf2ddd..389fd74 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ExistsPredicate.java
@@ -50,4 +50,11 @@
     ExistsPredicate another = (ExistsPredicate) expr;
     return not == another.not;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    ExistsPredicate exists = (ExistsPredicate) super.clone();
+    exists.not = not;
+    return exists;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Expr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Expr.java
index 26e0831..76af393 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Expr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Expr.java
@@ -25,7 +25,7 @@
 
 import java.lang.reflect.Type;
 
-public abstract class Expr implements JsonSerializable {
+public abstract class Expr implements JsonSerializable, Cloneable {
   @Expose @SerializedName("type")
   private static final String SERIALIZED_NAME_OF_OP_TYPE = "OpType";
   @Expose @SerializedName(SERIALIZED_NAME_OF_OP_TYPE)
@@ -85,6 +85,13 @@
 	}
 
   @Override
+  public Object clone() throws CloneNotSupportedException {
+    Expr newExpr = (Expr) super.clone();
+    newExpr.opType = opType;
+    return newExpr;
+  }
+
+  @Override
   public String toString() {
     return toJson();
   }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/FunctionExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/FunctionExpr.java
index b776cd2..333c845 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/FunctionExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/FunctionExpr.java
@@ -82,4 +82,17 @@
     return signature.equals(another.signature) &&
         TUtil.checkEquals(params, another.params);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    FunctionExpr func = (FunctionExpr) super.clone();
+    func.signature = signature;
+    if (params != null) {
+      func.params = new Expr[params.length];
+      for (int i = 0; i < params.length; i++) {
+        func.params[i] = (Expr) params[i].clone();
+      }
+    }
+    return func;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/GeneralSetFunctionExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/GeneralSetFunctionExpr.java
index 3d812e7..c10bd76 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/GeneralSetFunctionExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/GeneralSetFunctionExpr.java
@@ -34,7 +34,6 @@
    */
   protected GeneralSetFunctionExpr(OpType type, String signature, boolean distinct, Expr [] params) {
     super(type, signature, params);
-    Preconditions.checkArgument(OpType.isAggregationFunction(type));
     this.distinct = distinct;
   }
 
@@ -66,4 +65,11 @@
   public boolean equalsTo(Expr expr) {
     return distinct == ((GeneralSetFunctionExpr)expr).distinct;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    GeneralSetFunctionExpr newSetFunc = (GeneralSetFunctionExpr) super.clone();
+    newSetFunc.distinct = distinct;
+    return newSetFunc;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Having.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Having.java
index dbe6f21..5723496 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Having.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Having.java
@@ -62,4 +62,11 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Having having = (Having) super.clone();
+    having.qual = (Expr) qual.clone();
+    return having;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/InPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/InPredicate.java
index 52eb92a..51d89b7 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/InPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/InPredicate.java
@@ -41,4 +41,11 @@
   public Expr getInValue() {
     return right;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    InPredicate inPredicate = (InPredicate) super.clone();
+    inPredicate.not = not;
+    return inPredicate;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Insert.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Insert.java
index 3f15957..ce9b703 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Insert.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Insert.java
@@ -23,6 +23,7 @@
 import com.google.gson.annotations.SerializedName;
 import org.apache.tajo.util.TUtil;
 
+import java.util.HashMap;
 import java.util.Map;
 
 public class Insert extends Expr {
@@ -140,4 +141,17 @@
         TUtil.checkEquals(location, another.location) &&
         TUtil.checkEquals(params, another.params);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Insert insert = (Insert) super.clone();
+    insert.overwrite = overwrite;
+    insert.tableName = tableName;
+    insert.targetColumns = targetColumns != null ? targetColumns.clone() : null;
+    insert.storageType = storageType;
+    insert.location = location;
+    insert.subquery = (Expr) subquery.clone();
+    insert.params = new HashMap<String, String>(params);
+    return insert;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/IsNullPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IsNullPredicate.java
index f9f6e11..caa62c7 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/IsNullPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/IsNullPredicate.java
@@ -24,7 +24,7 @@
 
 public class IsNullPredicate extends UnaryOperator {
   @Expose @SerializedName("IsNot")
-  private final boolean not;
+  private boolean not;
 
   public IsNullPredicate(boolean not, Expr column) {
     super(OpType.IsNullPredicate);
@@ -49,4 +49,11 @@
     IsNullPredicate nullPredicate = (IsNullPredicate) expr;
     return not == nullPredicate.not;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    IsNullPredicate nullPredicate = (IsNullPredicate) super.clone();
+    nullPredicate.not = not;
+    return nullPredicate;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Join.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Join.java
index 2acdde3..2b1f344 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Join.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Join.java
@@ -91,4 +91,19 @@
   public String toJson() {
     return JsonHelper.toJson(this);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Join join = (Join) super.clone();
+    join.joinType = joinType;
+    join.joinQual = (Expr) joinQual.clone();
+    if (joinColumns != null) {
+      join.joinColumns = new ColumnReferenceExpr[joinColumns.length];
+      for (ColumnReferenceExpr colume : joinColumns) {
+        join.joinColumns = (ColumnReferenceExpr[]) colume.clone();
+      }
+    }
+    join.natural = natural;
+    return join;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Limit.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Limit.java
index 20dad6b..3a49ac7 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Limit.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Limit.java
@@ -55,4 +55,11 @@
     Limit another = (Limit) expr;
     return fetchFirstNum.equals(another.fetchFirstNum);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Limit limit = (Limit) super.clone();
+    limit.fetchFirstNum = (Expr) fetchFirstNum.clone();
+    return limit;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
index 7747ff9..d829f70 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/LiteralValue.java
@@ -87,4 +87,12 @@
       return LiteralType.String;
     }
   }
+
+  @Override
+  public LiteralValue clone() throws CloneNotSupportedException {
+    LiteralValue literal = (LiteralValue) super.clone();
+    literal.valueType = valueType;
+    literal.value = value;
+    return literal;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/NamedExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/NamedExpr.java
index 31e835f..7a0af05 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/NamedExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/NamedExpr.java
@@ -89,4 +89,11 @@
   public String toJson() {
     return JsonHelper.toJson(this);
   }
+
+  @Override
+  public NamedExpr clone() throws CloneNotSupportedException {
+    NamedExpr namedExpr = (NamedExpr) super.clone();
+    namedExpr.alias = alias;
+    return namedExpr;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
index bc6e89b..19c4ab5 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/OpType.java
@@ -94,11 +94,10 @@
   Column(ColumnReferenceExpr.class),
   Target(NamedExpr.class),
   Function(FunctionExpr.class),
-  WindowFunction(WindowFunctionExpr.class),
-  WindowSpec(WindowSpecExpr.class),
   Asterisk(QualifiedAsteriskExpr.class),
 
   // Set Functions
+  WindowFunction(WindowFunctionExpr.class),
   CountRowsFunction(CountRowsFunctionExpr.class),
   GeneralSetFunction(GeneralSetFunctionExpr.class),
 
@@ -142,13 +141,36 @@
     }
   }
 
+  public static boolean isLogicalType(OpType type) {
+    return type == Not || type == And || type == Or;
+  }
+
+  public static boolean isComparisonType(OpType type) {
+    return
+        type == OpType.Equals ||
+        type == OpType.NotEquals ||
+        type == OpType.LessThan ||
+        type == OpType.GreaterThan ||
+        type == OpType.LessThanOrEquals ||
+        type == OpType.GreaterThanOrEquals;
+  }
+
+  public static boolean isArithmeticType(OpType type) {
+    return
+        type == Plus ||
+        type == Minus ||
+        type == Multiply ||
+        type == Divide ||
+        type == Modular;
+  }
+
   /**
    * Check if it is one of the literal types.
    *
    * @param type The type to be checked
    * @return True if it is one of the literal types. Otherwise, it returns False.
    */
-  public static boolean isLiteral(OpType type) {
+  public static boolean isLiteralType(OpType type) {
     return  type == Literal ||
             type == NullLiteral ||
             type == TimeLiteral ||
@@ -163,7 +185,7 @@
    * @return True if it is aggregation function type. Otherwise, it returns False.
    */
   public static boolean isFunction(OpType type) {
-    return type == Function || isAggregationFunction(type);
+    return type == Function || isAggregationFunction(type) || isWindowFunction(type);
   }
 
   /**
@@ -175,4 +197,14 @@
   public static boolean isAggregationFunction(OpType type) {
     return type == GeneralSetFunction || type == CountRowsFunction;
   }
+
+  /**
+   * Check if it is an window function type.
+   *
+   * @param type The type to be checked
+   * @return True if it is window function type. Otherwise, it returns False.
+   */
+  public static boolean isWindowFunction(OpType type) {
+    return type == WindowFunction;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/PatternMatchPredicate.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/PatternMatchPredicate.java
index 2399e03..14c7d41 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/PatternMatchPredicate.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/PatternMatchPredicate.java
@@ -65,4 +65,12 @@
     return opType == another.opType &&
         not == another.not && caseInsensitive == another.caseInsensitive;
   }
+
+  @Override
+  public PatternMatchPredicate clone() throws CloneNotSupportedException {
+    PatternMatchPredicate patternMatchPredicate = (PatternMatchPredicate) super.clone();
+    patternMatchPredicate.not = not;
+    patternMatchPredicate.caseInsensitive = caseInsensitive;
+    return patternMatchPredicate;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Projection.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Projection.java
index 6ffc502..24c4399 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Projection.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Projection.java
@@ -69,10 +69,16 @@
     return JsonHelper.toJson(this);
   }
 
+  @Override
   public Projection clone() throws CloneNotSupportedException {
     Projection projection = (Projection)super.clone();
     projection.distinct = distinct;
-    projection.targets = targets;
+    if (targets != null) {
+      projection.targets = new NamedExpr[targets.length];
+      for (int i = 0; i < targets.length; i++) {
+        projection.targets[i] = targets[i].clone();
+      }
+    }
 
     return projection;
   }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/QualifiedAsteriskExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/QualifiedAsteriskExpr.java
index f067e59..e7ab208 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/QualifiedAsteriskExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/QualifiedAsteriskExpr.java
@@ -64,4 +64,11 @@
   public String toString() {
     return hasQualifier() ? qualifier + "." + ASTERISK : ASTERISK;
   }
+
+  @Override
+  public QualifiedAsteriskExpr clone() throws CloneNotSupportedException {
+    QualifiedAsteriskExpr qualifiedAsteriskExpr = (QualifiedAsteriskExpr) super.clone();
+    qualifiedAsteriskExpr.qualifier = qualifier;
+    return qualifiedAsteriskExpr;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
index c865454..2092b67 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Relation.java
@@ -74,4 +74,12 @@
     return TUtil.checkEquals(tableName, other.tableName) &&
         TUtil.checkEquals(alias, other.alias);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Relation relation = (Relation) super.clone();
+    relation.tableName = tableName;
+    relation.alias = alias;
+    return relation;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
index fdf6c94..ad7315b 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/RelationList.java
@@ -68,4 +68,14 @@
     Set<Expr> anotherSet = TUtil.newHashSet(another.relations);
     return thisSet.equals(anotherSet);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    RelationList relationList = (RelationList) super.clone();
+    relationList.relations = new Expr[relations.length];
+    for (int i = 0; i < relations.length; i++) {
+      relationList.relations[i] = (Expr) relations[i].clone();
+    }
+    return relationList;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Selection.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Selection.java
index 9cc245e..a2e6504 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Selection.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Selection.java
@@ -61,4 +61,11 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Selection selection = (Selection) super.clone();
+    selection.qual = (Expr) qual.clone();
+    return selection;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SetOperation.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SetOperation.java
index c20bf06..71ee1c8 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SetOperation.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SetOperation.java
@@ -52,4 +52,11 @@
     SetOperation another = (SetOperation) expr;
     return distinct == another.distinct;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SetOperation setOperation = (SetOperation) super.clone();
+    setOperation.distinct = distinct;
+    return setOperation;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
index 0c04341..c82b0f3 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/SignedExpr.java
@@ -44,4 +44,11 @@
   boolean equalsTo(Expr expr) {
     return negative == ((SignedExpr)expr).negative;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    SignedExpr signedExpr = (SignedExpr) super.clone();
+    signedExpr.negative = negative;
+    return signedExpr;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Sort.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Sort.java
index 0632b2e..7d5cb55 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Sort.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Sort.java
@@ -56,7 +56,17 @@
     return JsonHelper.toJson(this);
   }
 
-  public static class SortSpec {
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    Sort sort = (Sort) super.clone();
+    sort.sortSpecs = new SortSpec[sortSpecs.length];
+    for (int i = 0; i < sortSpecs.length; i++) {
+      sort.sortSpecs[i] = (SortSpec) sortSpecs[i].clone();
+    }
+    return sort;
+  }
+
+  public static class SortSpec implements Cloneable {
     @Expose @SerializedName("SortKey")
     private Expr key;
     @Expose @SerializedName("IsAsc")
@@ -98,6 +108,10 @@
       this.nullFirst = true;
     }
 
+    public void setKey(Expr expr) {
+      this.key = expr;
+    }
+
     public final Expr getKey() {
       return this.key;
     }
@@ -122,5 +136,14 @@
     public String toString() {
       return key + " " + (asc ? "asc" : "desc") + " " + (nullFirst ? "null first" :"");
     }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      SortSpec sortSpec = (SortSpec) super.clone();
+      sortSpec.key = (Expr) key.clone();
+      sortSpec.asc = asc;
+      sortSpec.nullFirst = nullFirst;
+      return sortSpec;
+    }
   }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
index 5c55164..6f08b0d 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TablePrimarySubQuery.java
@@ -63,4 +63,14 @@
   public String toJson() {
     return JsonHelper.toJson(this);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    TablePrimarySubQuery subQuery = (TablePrimarySubQuery) super.clone();
+    subQuery.subquery = (Expr) subquery.clone();
+    if (columnNames != null) {
+      subQuery.columnNames = columnNames.clone();
+    }
+    return subQuery;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeLiteral.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeLiteral.java
index 7baccd8..cba2293 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeLiteral.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeLiteral.java
@@ -51,4 +51,11 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    TimeLiteral timeLiteral = (TimeLiteral) super.clone();
+    timeLiteral.time = (TimeValue) timeLiteral.time.clone();
+    return timeLiteral;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeValue.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeValue.java
index 6ad75bc..cc8cc88 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeValue.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimeValue.java
@@ -23,7 +23,7 @@
 import com.google.gson.annotations.SerializedName;
 import org.apache.commons.lang.StringUtils;
 
-public class TimeValue {
+public class TimeValue implements Cloneable {
   @Expose @SerializedName("Hour")
   private String hours;
   @Expose @SerializedName("Minute")
@@ -82,4 +82,14 @@
   public int hashCode() {
     return Objects.hashCode(hours, minutes, seconds);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    TimeValue timeValue = (TimeValue) super.clone();
+    timeValue.hours = hours;
+    timeValue.minutes = minutes;
+    timeValue.seconds = seconds;
+    timeValue.secondsFraction = secondsFraction;
+    return timeValue;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimestampLiteral.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimestampLiteral.java
index 5ee20d3..a9ed6da 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimestampLiteral.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/TimestampLiteral.java
@@ -22,7 +22,7 @@
 import com.google.gson.annotations.Expose;
 import com.google.gson.annotations.SerializedName;
 
-public class TimestampLiteral extends Expr {
+public class TimestampLiteral extends Expr implements Cloneable {
   @Expose @SerializedName("Date")
   private DateValue date;
   @Expose @SerializedName("Time")
@@ -58,4 +58,12 @@
     }
     return false;
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    TimestampLiteral timestampLiteral = (TimestampLiteral) super.clone();
+    timestampLiteral.date = date;
+    timestampLiteral.time = time;
+    return timestampLiteral;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/UnaryOperator.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/UnaryOperator.java
index e266393..b85d58c 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/UnaryOperator.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/UnaryOperator.java
@@ -45,4 +45,11 @@
   public int hashCode() {
     return Objects.hashCode(getType(), child.hashCode());
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    UnaryOperator unaryOperator = (UnaryOperator) super.clone();
+    unaryOperator.child = (Expr) child.clone();
+    return unaryOperator;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ValueListExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ValueListExpr.java
index c5eafb6..32a5e82 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/ValueListExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/ValueListExpr.java
@@ -46,4 +46,14 @@
     ValueListExpr valueListExpr = (ValueListExpr) expr;
     return TUtil.checkEquals(values, valueListExpr.values);
   }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    ValueListExpr valueListExpr = (ValueListExpr) super.clone();
+    valueListExpr.values = new Expr[values.length];
+    for (int i = 0; i < values.length; i++) {
+      valueListExpr.values = (Expr[]) values[i].clone();
+    }
+    return valueListExpr;
+  }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Window.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Window.java
index e9cd21c..8ac90a4 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/Window.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/Window.java
@@ -46,9 +46,9 @@
 
   public static class WindowDefinition {
     private String windowName;
-    private WindowSpecExpr windowSpec;
+    private WindowSpec windowSpec;
 
-    public WindowDefinition(String windowName, WindowSpecExpr spec) {
+    public WindowDefinition(String windowName, WindowSpec spec) {
       this.windowName = windowName;
       this.windowSpec = spec;
     }
@@ -57,7 +57,7 @@
       return windowName;
     }
 
-    public WindowSpecExpr getWindowSpec() {
+    public WindowSpec getWindowSpec() {
       return windowSpec;
     }
   }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowFunctionExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowFunctionExpr.java
index 517ec0f..14ff930 100644
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowFunctionExpr.java
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowFunctionExpr.java
@@ -19,19 +19,20 @@
 package org.apache.tajo.algebra;
 
 import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
 import org.apache.tajo.util.TUtil;
 
-public class WindowFunctionExpr extends Expr {
-  // set function
-  GeneralSetFunctionExpr function;
+public class WindowFunctionExpr extends GeneralSetFunctionExpr {
 
   // over clause - only one of both is used.
+  @Expose @SerializedName("WindowName")
   private String windowName;
-  private WindowSpecExpr windowSpec;
+  @Expose @SerializedName("WindowSpec")
+  private WindowSpec windowSpec;
 
   public WindowFunctionExpr(GeneralSetFunctionExpr function) {
-    super(OpType.WindowFunction);
-    this.function = function;
+    super(OpType.WindowFunction, function.getSignature(), function.isDistinct(), function.getParams());
   }
 
   public boolean hasWindowName() {
@@ -50,11 +51,11 @@
     return windowSpec != null;
   }
 
-  public void setWindowSpec(WindowSpecExpr windowSpec) {
+  public void setWindowSpec(WindowSpec windowSpec) {
     this.windowSpec = windowSpec;
   }
 
-  public WindowSpecExpr getWindowSpec() {
+  public WindowSpec getWindowSpec() {
     return this.windowSpec;
   }
 
@@ -64,7 +65,7 @@
   }
 
   @Override
-  boolean equalsTo(Expr expr) {
+  public boolean equalsTo(Expr expr) {
     return TUtil.checkEquals(windowName, windowSpec);
   }
 }
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpec.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpec.java
new file mode 100644
index 0000000..da10b91
--- /dev/null
+++ b/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpec.java
@@ -0,0 +1,242 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.algebra;
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.util.TUtil;
+
+public class WindowSpec implements Cloneable {
+  @Expose private String windowName;
+  @Expose private Expr [] partitionKeys; // OVER (PARTITION BY ?,...,?)
+  @Expose private Sort.SortSpec [] sortSpecs; // OVER (... ORDER BY ?,...,?)
+  @Expose private WindowFrame windowFrame;
+
+  public boolean hasWindowName() {
+    return windowName != null;
+  }
+
+  public void setWindowName(String windowName) {
+    this.windowName = windowName;
+  }
+
+  public String getWindowName() {
+    return this.windowName;
+  }
+
+  public boolean hasPartitionBy() {
+    return this.partitionKeys != null;
+  }
+
+  public void setPartitionKeys(Expr[] partitionKeys) {
+    this.partitionKeys = partitionKeys;
+  }
+
+  public Expr [] getPartitionKeys() {
+    return this.partitionKeys;
+  }
+
+  public boolean hasOrderBy() {
+    return this.sortSpecs != null;
+  }
+
+  public void setSortSpecs(Sort.SortSpec [] sortSpecs) {
+    this.sortSpecs = sortSpecs;
+  }
+
+  public Sort.SortSpec [] getSortSpecs() {
+    return this.sortSpecs;
+  }
+
+  public boolean hasWindowFrame() {
+    return windowFrame != null;
+  }
+
+  public void setWindowFrame(WindowFrame frame) {
+    this.windowFrame = frame;
+  }
+
+  public WindowFrame getWindowFrame() {
+    return windowFrame;
+  }
+
+  public Object clone() throws CloneNotSupportedException {
+    WindowSpec windowSpec = (WindowSpec) super.clone();
+    windowSpec.windowName = windowName;
+    if (hasPartitionBy()) {
+      windowSpec.partitionKeys = new Expr[windowSpec.partitionKeys.length];
+      for (int i = 0; i < partitionKeys.length; i++) {
+        windowSpec.partitionKeys[i] = (Expr) partitionKeys[i].clone();
+      }
+    }
+    if (hasOrderBy()) {
+      windowSpec.sortSpecs = new Sort.SortSpec[sortSpecs.length];
+      for (int i = 0; i < sortSpecs.length; i++) {
+        windowSpec.sortSpecs[i] = (Sort.SortSpec) sortSpecs[i].clone();
+      }
+    }
+    if (hasWindowFrame()) {
+      windowSpec.windowFrame = (WindowFrame) windowFrame.clone();
+    }
+    return windowSpec;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hashCode(windowName, partitionKeys, sortSpecs);
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == this) {
+      return true;
+    }
+
+    if (obj instanceof WindowSpec) {
+      WindowSpec another = (WindowSpec) obj;
+      return TUtil.checkEquals(windowName, another.windowName) &&
+          TUtil.checkEquals(partitionKeys, another.partitionKeys) &&
+          TUtil.checkEquals(sortSpecs, another.sortSpecs) &&
+          TUtil.checkEquals(windowFrame, another.windowFrame);
+    } else {
+      return false;
+    }
+
+  }
+
+  public static enum WindowFrameUnit {
+    ROW,
+    RANGE
+  }
+
+  public static enum WindowFrameStartBoundType {
+    UNBOUNDED_PRECEDING,
+    CURRENT_ROW,
+    PRECEDING
+  }
+
+  public static enum WindowFrameEndBoundType {
+    UNBOUNDED_FOLLOWING,
+    CURRENT_ROW,
+    FOLLOWING
+  }
+
+  public static class WindowFrame implements Cloneable {
+    @Expose private WindowFrameUnit unit;
+    @Expose private WindowStartBound startBound;
+    @Expose private WindowEndBound endBound;
+
+    public WindowFrame(WindowFrameUnit unit, WindowStartBound startBound) {
+      this.unit = unit;
+      this.startBound = startBound;
+    }
+
+    public WindowFrame(WindowFrameUnit unit, WindowStartBound startBound, WindowEndBound endBound) {
+      this(unit, startBound);
+      this.endBound = endBound;
+    }
+
+    public WindowStartBound getStartBound() {
+      return startBound;
+    }
+
+    public boolean hasEndBound() {
+      return endBound != null;
+    }
+
+    public WindowEndBound getEndBound() {
+      return endBound;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      WindowFrame frame = (WindowFrame) super.clone();
+      frame.unit = unit;
+      frame.startBound = (WindowStartBound) startBound.clone();
+      frame.endBound = (WindowEndBound) endBound.clone();
+      return frame;
+    }
+  }
+
+  public static class WindowStartBound implements Cloneable {
+    @Expose private WindowFrameStartBoundType boundType;
+    @Expose private Expr number;
+
+    public WindowStartBound(WindowFrameStartBoundType type) {
+      this.boundType = type;
+    }
+
+    public WindowFrameStartBoundType getBoundType() {
+      return boundType;
+    }
+
+    public boolean hasNumber() {
+      return this.number != null;
+    }
+
+    public void setNumber(Expr number) {
+      this.number = number;
+    }
+
+    public Expr getNumber() {
+      return number;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      WindowStartBound start = (WindowStartBound) super.clone();
+      start.boundType = boundType;
+      start.number = (Expr) number.clone();
+      return start;
+    }
+  }
+
+  public static class WindowEndBound implements Cloneable {
+    @Expose private WindowFrameEndBoundType boundType;
+    @Expose private Expr number;
+
+    public WindowEndBound(WindowFrameEndBoundType type) {
+      this.boundType = type;
+    }
+
+    public WindowFrameEndBoundType getBoundType() {
+      return boundType;
+    }
+
+    public boolean hasNumber() {
+      return this.number != null;
+    }
+
+    public void setNumber(Expr number) {
+      this.number = number;
+    }
+
+    public Expr getNumber() {
+      return number;
+    }
+
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+      WindowEndBound end = (WindowEndBound) super.clone();
+      end.boundType = boundType;
+      end.number = (Expr) number.clone();
+      return end;
+    }
+  }
+}
diff --git a/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpecExpr.java b/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpecExpr.java
deleted file mode 100644
index 37c2ff1..0000000
--- a/tajo-algebra/src/main/java/org/apache/tajo/algebra/WindowSpecExpr.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.tajo.algebra;
-
-import com.google.common.base.Objects;
-import org.apache.tajo.util.TUtil;
-
-public class WindowSpecExpr extends Expr {
-  private String windowName;
-  private Expr [] partitionKeys; // OVER (PARTITION BY ?,...,?)
-  private Sort.SortSpec [] sortSpecs; // OVER (... ORDER BY ?,...,?)
-  private WindowFrame windowFrame;
-
-  public WindowSpecExpr() {
-    super(OpType.WindowSpec);
-  }
-
-  public boolean hasWindowName() {
-    return windowName != null;
-  }
-
-  public void setWindowName(String windowName) {
-    this.windowName = windowName;
-  }
-
-  public String getWindowName() {
-    return this.windowName;
-  }
-
-  public boolean hasPartitionBy() {
-    return this.partitionKeys != null;
-  }
-
-  public void setPartitionKeys(Expr[] partitionKeys) {
-    this.partitionKeys = partitionKeys;
-  }
-
-  public Expr [] getPartitionKeys() {
-    return this.partitionKeys;
-  }
-
-  public boolean hasOrderBy() {
-    return this.sortSpecs != null;
-  }
-
-  public void setSortSpecs(Sort.SortSpec [] sortSpecs) {
-    this.sortSpecs = sortSpecs;
-  }
-
-  public Sort.SortSpec [] getSortSpecs() {
-    return this.sortSpecs;
-  }
-
-  public boolean hasWindowFrame() {
-    return windowFrame != null;
-  }
-
-  public void setWindowFrame(WindowFrame frame) {
-    this.windowFrame = frame;
-  }
-
-  public WindowFrame getWindowFrame() {
-    return windowFrame;
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hashCode(windowName, partitionKeys, sortSpecs);
-  }
-
-  @Override
-  boolean equalsTo(Expr expr) {
-    WindowSpecExpr another = (WindowSpecExpr) expr;
-    return TUtil.checkEquals(windowName, another.windowName) &&
-        TUtil.checkEquals(partitionKeys, another.partitionKeys) &&
-        TUtil.checkEquals(sortSpecs, another.sortSpecs);
-  }
-
-  public static enum WindowFrameUnit {
-    ROW,
-    RANGE
-  }
-
-  public static enum WindowFrameStartBoundType {
-    UNBOUNDED_PRECEDING,
-    CURRENT_ROW,
-    PRECEDING
-  }
-
-  public static enum WindowFrameEndBoundType {
-    UNBOUNDED_FOLLOWING,
-    CURRENT_ROW,
-    FOLLOWING
-  }
-
-  public static class WindowFrame {
-    WindowFrameUnit unit;
-    private WindowStartBound startBound;
-    private WindowEndBound endBound;
-
-    public WindowFrame(WindowFrameUnit unit, WindowStartBound startBound) {
-      this.unit = unit;
-      this.startBound = startBound;
-    }
-
-    public WindowFrame(WindowFrameUnit unit, WindowStartBound startBound, WindowEndBound endBound) {
-      this(unit, startBound);
-      this.endBound = endBound;
-    }
-
-    public WindowStartBound getStartBound() {
-      return startBound;
-    }
-
-    public boolean hasEndBound() {
-      return endBound != null;
-    }
-
-    public WindowEndBound getEndBound() {
-      return endBound;
-    }
-  }
-
-  public static class WindowStartBound {
-    private WindowFrameStartBoundType boundType;
-    private Expr number;
-
-    public WindowStartBound(WindowFrameStartBoundType type) {
-      this.boundType = type;
-    }
-
-    public WindowFrameStartBoundType getBoundType() {
-      return boundType;
-    }
-
-    public void setNumber(Expr number) {
-      this.number = number;
-    }
-
-    public Expr getNumber() {
-      return number;
-    }
-  }
-
-  public static class WindowEndBound {
-    private WindowFrameEndBoundType boundType;
-    private Expr number;
-
-    public WindowEndBound(WindowFrameEndBoundType type) {
-      this.boundType = type;
-    }
-
-    public WindowFrameEndBoundType getBoundType() {
-      return boundType;
-    }
-
-    public Expr setNumber(Expr number) {
-      return number;
-    }
-
-    public Expr getNumber() {
-      return number;
-    }
-  }
-}
diff --git a/tajo-catalog/tajo-catalog-common/src/main/java/org/apache/tajo/catalog/exception/NoSuchFunctionException.java b/tajo-catalog/tajo-catalog-common/src/main/java/org/apache/tajo/catalog/exception/NoSuchFunctionException.java
index 78df08f..d109470 100644
--- a/tajo-catalog/tajo-catalog-common/src/main/java/org/apache/tajo/catalog/exception/NoSuchFunctionException.java
+++ b/tajo-catalog/tajo-catalog-common/src/main/java/org/apache/tajo/catalog/exception/NoSuchFunctionException.java
@@ -26,6 +26,10 @@
 public class NoSuchFunctionException extends RuntimeException {
 	private static final long serialVersionUID = 5062193018697228028L;
 
+  public NoSuchFunctionException(String message) {
+    super(message);
+  }
+
   public NoSuchFunctionException(String funcName, TajoDataTypes.DataType [] parameters) {
     super("function " + CatalogUtil.getCanonicalSignature(funcName, parameters) + " does not exist");
   }
diff --git a/tajo-catalog/tajo-catalog-common/src/main/proto/CatalogProtos.proto b/tajo-catalog/tajo-catalog-common/src/main/proto/CatalogProtos.proto
index e70ed2b..367d0b8 100644
--- a/tajo-catalog/tajo-catalog-common/src/main/proto/CatalogProtos.proto
+++ b/tajo-catalog/tajo-catalog-common/src/main/proto/CatalogProtos.proto
@@ -119,9 +119,11 @@
   GENERAL = 0;
   AGGREGATION = 1;
   DISTINCT_AGGREGATION = 2;
-  UDF = 3;
-  UDA = 4;
-  DISTINCT_UDA = 5;
+  WINDOW = 3;
+  UDF = 4;
+  UDA = 5;
+  DISTINCT_UDA = 6;
+  WINDOW_UDA = 7;
 }
 
 message FunctionDescProto {
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AggregationFunctionCallEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AggregationFunctionCallEval.java
index 10eadce..ab18aa9 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AggregationFunctionCallEval.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AggregationFunctionCallEval.java
@@ -33,6 +33,11 @@
   @Expose boolean firstPhase = false;
   private Tuple params;
 
+  protected AggregationFunctionCallEval(EvalType type, FunctionDesc desc, AggFunction instance, EvalNode[] givenArgs) {
+    super(type, desc, givenArgs);
+    this.instance = instance;
+  }
+
   public AggregationFunctionCallEval(FunctionDesc desc, AggFunction instance, EvalNode[] givenArgs) {
     super(EvalType.AGG_FUNCTION, desc, givenArgs);
     this.instance = instance;
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java
index d993b27..a72e2a8 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/AlgebraicUtil.java
@@ -37,7 +37,7 @@
    * @return Transposed expression
    */
   public static EvalNode transpose(EvalNode evalNode, Column target) {
-    BinaryEval commutated = null;
+    BinaryEval commutated;
 
     if (evalNode instanceof BinaryEval) { // if it is binary
       BinaryEval binaryEval = (BinaryEval) evalNode;
@@ -168,19 +168,22 @@
       return unaryEval;
     }
 
-    public EvalNode visitFuncCall(Object context, GeneralFunctionEval evalNode, Stack<EvalNode> stack) {
-      boolean constant = true;
+    @Override
+    public EvalNode visitFuncCall(Object context, FunctionEval evalNode, Stack<EvalNode> stack) {
+      boolean constantOfAllDescendents = true;
 
       if ("sleep".equals(evalNode.funcDesc.getSignature())) {
-        constant = false;
+        constantOfAllDescendents = false;
       } else {
-        for (EvalNode arg : evalNode.getArgs()) {
-          arg = visit(context, arg, stack);
-          constant &= (arg.getType() == EvalType.CONST);
+        if (evalNode.getArgs() != null) {
+          for (EvalNode arg : evalNode.getArgs()) {
+            arg = visit(context, arg, stack);
+            constantOfAllDescendents &= (arg.getType() == EvalType.CONST);
+          }
         }
       }
 
-      if (constant) {
+      if (constantOfAllDescendents && evalNode.getType() == EvalType.FUNCTION) {
         return new ConstEval(evalNode.eval(null, null));
       } else {
         return evalNode;
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BasicEvalNodeVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BasicEvalNodeVisitor.java
index 6e83c70..3b94cc9 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BasicEvalNodeVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BasicEvalNodeVisitor.java
@@ -125,6 +125,9 @@
       case AGG_FUNCTION:
         result = visitAggrFuncCall(context, (AggregationFunctionCallEval) evalNode, stack);
         break;
+      case WINDOW_FUNCTION:
+        result = visitWindowFunc(context, (WindowFunctionEval) evalNode, stack);
+      break;
 
       case SIGNED:
         result = visitSigned(context, (SignedEval) evalNode, stack);
@@ -326,6 +329,11 @@
   }
 
   @Override
+  public RESULT visitWindowFunc(CONTEXT context, WindowFunctionEval evalNode, Stack<EvalNode> stack) {
+    return visitDefaultFunctionEval(context, evalNode, stack);
+  }
+
+  @Override
   public RESULT visitSigned(CONTEXT context, SignedEval signedEval, Stack<EvalNode> stack) {
     return visitDefaultUnaryEval(context, signedEval, stack);
   }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
index bafce91..70d6bb1 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/BinaryEval.java
@@ -26,6 +26,7 @@
 import org.apache.tajo.datum.Datum;
 import org.apache.tajo.datum.DatumFactory;
 import org.apache.tajo.datum.NullDatum;
+import org.apache.tajo.engine.utils.DataTypeUtil;
 import org.apache.tajo.exception.InvalidOperationException;
 import org.apache.tajo.storage.Tuple;
 
@@ -60,7 +61,7 @@
             type == EvalType.MULTIPLY ||
             type == EvalType.DIVIDE ||
             type == EvalType.MODULAR ) {
-      this.returnType = determineType(left.getValueType(), right.getValueType());
+      this.returnType = DataTypeUtil.determineType(left.getValueType(), right.getValueType());
 
     } else if (type == EvalType.CONCATENATE) {
       this.returnType = CatalogUtil.newSimpleDataType(Type.TEXT);
@@ -97,102 +98,6 @@
     }
   }
 
-  /**
-   * This is verified by ExprsVerifier.checkArithmeticOperand().
-   */
-  private DataType determineType(DataType left, DataType right) throws InvalidEvalException {
-    switch (left.getType()) {
-
-    case INT1:
-    case INT2:
-    case INT4: {
-      switch(right.getType()) {
-      case INT1:
-      case INT2:
-      case INT4: return CatalogUtil.newSimpleDataType(Type.INT4);
-      case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
-      case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
-      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
-      case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    case INT8: {
-      switch(right.getType()) {
-      case INT1:
-      case INT2:
-      case INT4:
-      case INT8: return CatalogUtil.newSimpleDataType(Type.INT8);
-      case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
-      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
-      case DATE: return CatalogUtil.newSimpleDataType(Type.DATE);
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    case FLOAT4: {
-      switch(right.getType()) {
-      case INT1:
-      case INT2:
-      case INT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
-      case INT8: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
-      case FLOAT4: return CatalogUtil.newSimpleDataType(Type.FLOAT4);
-      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    case FLOAT8: {
-      switch(right.getType()) {
-      case INT1:
-      case INT2:
-      case INT4:
-      case INT8:
-      case FLOAT4:
-      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.FLOAT8);
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    case DATE: {
-      switch(right.getType()) {
-      case INT2:
-      case INT4:
-      case INT8: return CatalogUtil.newSimpleDataType(Type.DATE);
-      case INTERVAL:
-      case TIME: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
-      case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
-      }
-    }
-
-    case TIME: {
-      switch(right.getType()) {
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIME);
-      case TIME: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      case DATE: return CatalogUtil.newSimpleDataType(Type.INT4);
-      }
-    }
-
-    case TIMESTAMP: {
-      switch (right.getType()) {
-      case INTERVAL: return CatalogUtil.newSimpleDataType(Type.TIMESTAMP);
-      case TIMESTAMP: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    case INTERVAL: {
-      switch (right.getType()) {
-      case INTERVAL:
-      case FLOAT4:
-      case FLOAT8: return CatalogUtil.newSimpleDataType(Type.INTERVAL);
-      }
-    }
-
-    default: return left;
-    }
-  }
-
   @Override
   public Datum eval(Schema schema, Tuple tuple) {
     Datum lhs = leftExpr.eval(schema, tuple);
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNodeVisitor2.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNodeVisitor2.java
index 024a988..e85984e 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNodeVisitor2.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalNodeVisitor2.java
@@ -64,6 +64,7 @@
   // Functions
   RESULT visitFuncCall(CONTEXT context, GeneralFunctionEval evalNode, Stack<EvalNode> stack);
   RESULT visitAggrFuncCall(CONTEXT context, AggregationFunctionCallEval evalNode, Stack<EvalNode> stack);
+  RESULT visitWindowFunc(CONTEXT context, WindowFunctionEval evalNode, Stack<EvalNode> stack);
 
   RESULT visitSigned(CONTEXT context, SignedEval signedEval, Stack<EvalNode> stack);
 
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java
index 14367f4..549f8d0 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/EvalType.java
@@ -44,6 +44,7 @@
   BIT_XOR(BinaryEval.class, "|"),
 
   // Function
+  WINDOW_FUNCTION(WindowFunctionEval.class),
   AGG_FUNCTION(AggregationFunctionCallEval.class),
   FUNCTION(GeneralFunctionEval.class),
 
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java
index 6781c34..0cc8d98 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/FunctionEval.java
@@ -19,6 +19,7 @@
 package org.apache.tajo.engine.eval;
 
 import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
 import com.google.gson.annotations.Expose;
 import org.apache.tajo.catalog.FunctionDesc;
 import org.apache.tajo.catalog.Schema;
@@ -42,7 +43,8 @@
 	public FunctionEval(EvalType type, FunctionDesc funcDesc, EvalNode[] argEvals) {
 		super(type);
 		this.funcDesc = funcDesc;
-		this.argEvals = argEvals;
+    Preconditions.checkArgument(argEvals != null, "argEvals cannot be null");
+    this.argEvals = argEvals;
 	}
 
   public ParamType [] getParamType() {
@@ -85,6 +87,10 @@
 		return funcDesc.getSignature();
 	}
 
+  public FunctionDesc getFuncDesc() {
+    return this.funcDesc;
+  }
+
   @Override
 	public String toString() {
 		StringBuilder sb = new StringBuilder();
@@ -119,26 +125,32 @@
   public Object clone() throws CloneNotSupportedException {
     FunctionEval eval = (FunctionEval) super.clone();
     eval.funcDesc = (FunctionDesc) funcDesc.clone();
-    eval.argEvals = new EvalNode[argEvals.length];
-    for (int i = 0; i < argEvals.length; i++) {
-      eval.argEvals[i] = (EvalNode) argEvals[i].clone();
-    }    
+    if (argEvals != null) {
+      eval.argEvals = new EvalNode[argEvals.length];
+      for (int i = 0; i < argEvals.length; i++) {
+        eval.argEvals[i] = (EvalNode) argEvals[i].clone();
+      }
+    }
     return eval;
   }
 	
 	@Override
   public void preOrder(EvalNodeVisitor visitor) {
-    for (EvalNode eval : argEvals) {
-      eval.postOrder(visitor);
+    if (argEvals != null) {
+      for (EvalNode eval : argEvals) {
+        eval.postOrder(visitor);
+      }
     }
     visitor.visit(this);
   }
 	
 	@Override
 	public void postOrder(EvalNodeVisitor visitor) {
-	  for (EvalNode eval : argEvals) {
-	    eval.postOrder(visitor);
-	  }
+    if (argEvals != null) {
+      for (EvalNode eval : argEvals) {
+        eval.postOrder(visitor);
+      }
+    }
 	  visitor.visit(this);
 	}
 }
\ No newline at end of file
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java
index e4503e2..93f1f74 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/SimpleEvalNodeVisitor.java
@@ -66,10 +66,13 @@
 
       // Functions
       case FUNCTION:
-        result = visitFuncCall(context, (GeneralFunctionEval) evalNode, stack);
+        result = visitFuncCall(context, (FunctionEval) evalNode, stack);
         break;
       case AGG_FUNCTION:
-        result = visitAggrFuncCall(context, (AggregationFunctionCallEval) evalNode, stack);
+        result = visitFuncCall(context, (FunctionEval) evalNode, stack);
+        break;
+      case WINDOW_FUNCTION:
+        result = visitFuncCall(context, (FunctionEval) evalNode, stack);
         break;
 
       default:
@@ -163,11 +166,7 @@
   // Functions
   ///////////////////////////////////////////////////////////////////////////////////////////////
 
-  public EvalNode visitFuncCall(CONTEXT context, GeneralFunctionEval evalNode, Stack<EvalNode> stack) {
-    return visitDefaultFunctionEval(context, stack, evalNode);
-  }
-
-  public EvalNode visitAggrFuncCall(CONTEXT context, AggregationFunctionCallEval evalNode, Stack<EvalNode> stack) {
+  public EvalNode visitFuncCall(CONTEXT context, FunctionEval evalNode, Stack<EvalNode> stack) {
     return visitDefaultFunctionEval(context, stack, evalNode);
   }
 }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/eval/WindowFunctionEval.java b/tajo-core/src/main/java/org/apache/tajo/engine/eval/WindowFunctionEval.java
new file mode 100644
index 0000000..fb4eede
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/eval/WindowFunctionEval.java
@@ -0,0 +1,117 @@
+/**
+ * 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.eval;
+
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.FunctionDesc;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.common.TajoDataTypes.DataType;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.engine.function.AggFunction;
+import org.apache.tajo.engine.function.FunctionContext;
+import org.apache.tajo.engine.planner.logical.WindowSpec;
+import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.storage.VTuple;
+import org.apache.tajo.util.TUtil;
+
+public class WindowFunctionEval extends AggregationFunctionCallEval implements Cloneable {
+  @Expose private SortSpec [] sortSpecs;
+  @Expose WindowSpec.WindowFrame windowFrame;
+  private Tuple params;
+
+  public WindowFunctionEval(FunctionDesc desc, AggFunction instance, EvalNode[] givenArgs,
+                            WindowSpec.WindowFrame windowFrame) {
+    super(EvalType.WINDOW_FUNCTION, desc, instance, givenArgs);
+    this.windowFrame = windowFrame;
+  }
+
+  public boolean hasSortSpecs() {
+    return sortSpecs != null;
+  }
+
+  public void setSortSpecs(SortSpec [] sortSpecs) {
+    this.sortSpecs = sortSpecs;
+  }
+
+  public SortSpec [] getSortSpecs() {
+    return sortSpecs;
+  }
+
+  public WindowSpec.WindowFrame getWindowFrame() {
+    return windowFrame;
+  }
+
+  @Override
+  public Datum eval(Schema schema, Tuple tuple) {
+    throw new UnsupportedOperationException("Cannot execute eval() of aggregation function");
+  }
+
+  public void merge(FunctionContext context, Schema schema, Tuple tuple) {
+    if (params == null) {
+      this.params = new VTuple(argEvals.length);
+    }
+
+    if (argEvals != null) {
+      for (int i = 0; i < argEvals.length; i++) {
+        params.put(i, argEvals[i].eval(schema, tuple));
+      }
+    }
+
+    instance.eval(context, params);
+  }
+
+  public Datum terminate(FunctionContext context) {
+    return instance.terminate(context);
+  }
+
+  @Override
+  public DataType getValueType() {
+    return funcDesc.getReturnType();
+  }
+
+  @Override
+  public Object clone() throws CloneNotSupportedException {
+    WindowFunctionEval windowFunctionEval = (WindowFunctionEval) super.clone();
+    if (sortSpecs != null) {
+      windowFunctionEval.sortSpecs = new SortSpec[sortSpecs.length];
+      for (int i = 0; i < sortSpecs.length; i++) {
+        windowFunctionEval.sortSpecs[i] = (SortSpec) sortSpecs[i].clone();
+      }
+    }
+    return windowFunctionEval;
+  }
+
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    if (argEvals != null) {
+      for(int i=0; i < argEvals.length; i++) {
+        sb.append(argEvals[i]);
+        if(i+1 < argEvals.length)
+          sb.append(",");
+      }
+    }
+    sb.append(funcDesc.getSignature()).append("(").append(isDistinct() ? " distinct" : "").append(sb)
+        .append(")");
+    if (hasSortSpecs()) {
+      sb.append("ORDER BY ").append(TUtil.arrayToString(sortSpecs));
+    }
+    return sb.toString();
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/function/WindowAggFunc.java b/tajo-core/src/main/java/org/apache/tajo/engine/function/WindowAggFunc.java
new file mode 100644
index 0000000..164738a
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/function/WindowAggFunc.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.engine.function;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.json.CatalogGsonHelper;
+import org.apache.tajo.catalog.proto.CatalogProtos;
+import org.apache.tajo.common.TajoDataTypes.DataType;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.exception.InvalidOperationException;
+import org.apache.tajo.storage.Tuple;
+
+public abstract class WindowAggFunc<T extends Datum> extends AggFunction<T> {
+
+  public WindowAggFunc(Column[] definedArgs) {
+    super(definedArgs);
+  }
+
+  public abstract FunctionContext newContext();
+
+  public abstract void eval(FunctionContext ctx, Tuple params);
+
+  public void merge(FunctionContext ctx, Tuple part) {
+    throw new InvalidOperationException("Window function does not support getPartialResult()");
+  }
+
+  public Datum getPartialResult(FunctionContext ctx) {
+    throw new InvalidOperationException("Window function does not support getPartialResult()");
+  }
+
+  public DataType getPartialResultType() {
+    throw new InvalidOperationException("Window function does not support getPartialResultType()");
+  }
+
+  public abstract T terminate(FunctionContext ctx);
+
+  @Override
+  public String toJson() {
+    return CatalogGsonHelper.toJson(this, WindowAggFunc.class);
+  }
+
+  @Override
+  public CatalogProtos.FunctionType getFunctionType() {
+    return CatalogProtos.FunctionType.WINDOW;
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/function/window/Rank.java b/tajo-core/src/main/java/org/apache/tajo/engine/function/window/Rank.java
new file mode 100644
index 0000000..e461f9d
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/function/window/Rank.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.function.window;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.proto.CatalogProtos;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.datum.Int8Datum;
+import org.apache.tajo.engine.function.FunctionContext;
+import org.apache.tajo.engine.function.WindowAggFunc;
+import org.apache.tajo.engine.function.annotation.Description;
+import org.apache.tajo.engine.function.annotation.ParamTypes;
+import org.apache.tajo.storage.Tuple;
+
+@Description(
+    functionName = "rank",
+    description = " The number of rows for "
+        + "which the supplied expressions are unique and non-NULL.",
+    example = "> SELECT rank() OVER (ORDER BY x) FROM ...;",
+    returnType = TajoDataTypes.Type.INT8,
+    paramTypes = {@ParamTypes(paramTypes = {})}
+)
+public final class Rank extends WindowAggFunc {
+
+  public Rank() {
+    super(new Column[] {
+        new Column("expr", TajoDataTypes.Type.ANY)
+    });
+  }
+
+  public static boolean checkIfDistinctValue(RankContext context, Tuple params) {
+    for (int i = 0; i < context.latest.length; i++) {
+      if (!context.latest[i].equalsTo(params.get(i)).isTrue()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  public void eval(FunctionContext context, Tuple params) {
+    RankContext ctx = (RankContext) context;
+
+    if ((ctx.latest == null || checkIfDistinctValue(ctx, params))) {
+      ctx.rank = ctx.accumulatedCount;
+      ctx.latest = params.getValues().clone();
+    }
+    ctx.accumulatedCount++;
+  }
+
+  @Override
+  public Int8Datum terminate(FunctionContext ctx) {
+    return DatumFactory.createInt8(((RankContext) ctx).rank);
+  }
+
+  @Override
+  public FunctionContext newContext() {
+    return new RankContext();
+  }
+
+  private class RankContext implements FunctionContext {
+    long rank = 0;
+    long accumulatedCount = 1;
+    Datum [] latest = null;
+  }
+
+  @Override
+  public CatalogProtos.FunctionType getFunctionType() {
+    return CatalogProtos.FunctionType.WINDOW;
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/function/window/RowNumber.java b/tajo-core/src/main/java/org/apache/tajo/engine/function/window/RowNumber.java
new file mode 100644
index 0000000..8b0943f
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/function/window/RowNumber.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.function.window;
+
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.common.TajoDataTypes.Type;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.datum.DatumFactory;
+import org.apache.tajo.datum.Int8Datum;
+import org.apache.tajo.engine.function.FunctionContext;
+import org.apache.tajo.engine.function.WindowAggFunc;
+import org.apache.tajo.engine.function.annotation.Description;
+import org.apache.tajo.engine.function.annotation.ParamTypes;
+import org.apache.tajo.storage.Tuple;
+
+@Description(
+  functionName = "row_number",
+  description = "the total number of retrieved rows",
+  example = "> SELECT row_number() OVER ();",
+  returnType = Type.INT8,
+  paramTypes = {@ParamTypes(paramTypes = {})}
+)
+public class RowNumber extends WindowAggFunc<Datum> {
+
+  public RowNumber() {
+    super(NoArgs);
+  }
+
+  protected RowNumber(Column[] columns) {
+    super(columns);
+  }
+
+  @Override
+  public FunctionContext newContext() {
+    return new RowNumberContext();
+  }
+
+  @Override
+  public void eval(FunctionContext ctx, Tuple params) {
+    ((RowNumberContext) ctx).count++;
+  }
+
+  @Override
+  public Int8Datum terminate(FunctionContext ctx) {
+    return DatumFactory.createInt8(((RowNumberContext) ctx).count);
+  }
+
+  protected class RowNumberContext implements FunctionContext {
+    long count = 0;
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
index 59a9b46..580ec61 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/parser/SQLAnalyzer.java
@@ -36,8 +36,8 @@
 
 import static org.apache.tajo.algebra.Aggregation.GroupElement;
 import static org.apache.tajo.algebra.CreateTable.*;
-import static org.apache.tajo.algebra.WindowSpecExpr.WindowFrameEndBoundType;
-import static org.apache.tajo.algebra.WindowSpecExpr.WindowFrameStartBoundType;
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameEndBoundType;
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameStartBoundType;
 import static org.apache.tajo.common.TajoDataTypes.Type;
 import static org.apache.tajo.engine.parser.SQLParser.*;
 
@@ -347,7 +347,7 @@
     if (checkIfExist(windowNameOrSpec.window_name())) {
       windowFunction.setWindowName(windowNameOrSpec.window_name().getText());
     } else {
-      windowFunction.setWindowSpec(visitWindow_specification(windowNameOrSpec.window_specification()));
+      windowFunction.setWindowSpec(buildWindowSpec(windowNameOrSpec.window_specification()));
     }
 
     return windowFunction;
@@ -360,14 +360,14 @@
     for (int i = 0; i < definitions.length; i++) {
       Window_definitionContext windowDefinitionContext = ctx.window_definition_list().window_definition(i);
       String windowName = windowDefinitionContext.window_name().identifier().getText();
-      WindowSpecExpr windowSpec = visitWindow_specification(windowDefinitionContext.window_specification());
+      WindowSpec windowSpec = buildWindowSpec(windowDefinitionContext.window_specification());
       definitions[i] = new Window.WindowDefinition(windowName, windowSpec);
     }
     return new Window(definitions);
   }
 
-  @Override public WindowSpecExpr visitWindow_specification(@NotNull SQLParser.Window_specificationContext ctx) {
-    WindowSpecExpr windowSpec = new WindowSpecExpr();
+  public WindowSpec buildWindowSpec(SQLParser.Window_specificationContext ctx) {
+    WindowSpec windowSpec = new WindowSpec();
     if (checkIfExist(ctx.window_specification_details())) {
       Window_specification_detailsContext windowSpecDetail = ctx.window_specification_details();
 
@@ -388,26 +388,26 @@
       if (checkIfExist(windowSpecDetail.window_frame_clause())) {
         Window_frame_clauseContext frameContext = windowSpecDetail.window_frame_clause();
 
-        WindowSpecExpr.WindowFrameUnit unit;
+        WindowSpec.WindowFrameUnit unit;
         // frame unit - there are only two cases: RANGE and ROW
         if (checkIfExist(frameContext.window_frame_units().RANGE())) {
-          unit = WindowSpecExpr.WindowFrameUnit.RANGE;
+          unit = WindowSpec.WindowFrameUnit.RANGE;
         } else {
-          unit = WindowSpecExpr.WindowFrameUnit.ROW;
+          unit = WindowSpec.WindowFrameUnit.ROW;
         }
 
-        WindowSpecExpr.WindowFrame windowFrame;
+        WindowSpec.WindowFrame windowFrame;
 
         if (checkIfExist(frameContext.window_frame_extent().window_frame_between())) { // when 'between' is given
           Window_frame_betweenContext between = frameContext.window_frame_extent().window_frame_between();
-          WindowSpecExpr.WindowStartBound startBound = buildWindowStartBound(between.window_frame_start_bound());
-          WindowSpecExpr.WindowEndBound endBound = buildWindowEndBound(between.window_frame_end_bound());
+          WindowSpec.WindowStartBound startBound = buildWindowStartBound(between.window_frame_start_bound());
+          WindowSpec.WindowEndBound endBound = buildWindowEndBound(between.window_frame_end_bound());
 
-          windowFrame = new WindowSpecExpr.WindowFrame(unit, startBound, endBound);
+          windowFrame = new WindowSpec.WindowFrame(unit, startBound, endBound);
         } else { // if there is only start bound
-          WindowSpecExpr.WindowStartBound startBound =
+          WindowSpec.WindowStartBound startBound =
               buildWindowStartBound(frameContext.window_frame_extent().window_frame_start_bound());
-          windowFrame = new WindowSpecExpr.WindowFrame(unit, startBound);
+          windowFrame = new WindowSpec.WindowFrame(unit, startBound);
         }
 
         windowSpec.setWindowFrame(windowFrame);
@@ -416,7 +416,7 @@
     return windowSpec;
   }
 
-  public WindowSpecExpr.WindowStartBound buildWindowStartBound(Window_frame_start_boundContext context) {
+  public WindowSpec.WindowStartBound buildWindowStartBound(Window_frame_start_boundContext context) {
     WindowFrameStartBoundType boundType = null;
     if (checkIfExist(context.UNBOUNDED())) {
       boundType = WindowFrameStartBoundType.UNBOUNDED_PRECEDING;
@@ -426,7 +426,7 @@
       boundType = WindowFrameStartBoundType.CURRENT_ROW;
     }
 
-    WindowSpecExpr.WindowStartBound bound = new WindowSpecExpr.WindowStartBound(boundType);
+    WindowSpec.WindowStartBound bound = new WindowSpec.WindowStartBound(boundType);
     if (boundType == WindowFrameStartBoundType.PRECEDING) {
       bound.setNumber(visitUnsigned_value_specification(context.unsigned_value_specification()));
     }
@@ -434,7 +434,7 @@
     return bound;
   }
 
-  public WindowSpecExpr.WindowEndBound buildWindowEndBound(Window_frame_end_boundContext context) {
+  public WindowSpec.WindowEndBound buildWindowEndBound(Window_frame_end_boundContext context) {
     WindowFrameEndBoundType boundType;
     if (checkIfExist(context.UNBOUNDED())) {
       boundType = WindowFrameEndBoundType.UNBOUNDED_FOLLOWING;
@@ -444,7 +444,7 @@
       boundType = WindowFrameEndBoundType.CURRENT_ROW;
     }
 
-    WindowSpecExpr.WindowEndBound endBound = new WindowSpecExpr.WindowEndBound(boundType);
+    WindowSpec.WindowEndBound endBound = new WindowSpec.WindowEndBound(boundType);
     if (boundType == WindowFrameEndBoundType.FOLLOWING) {
       endBound.setNumber(visitUnsigned_value_specification(context.unsigned_value_specification()));
     }
@@ -1275,7 +1275,7 @@
       return new ListPartition(buildColumnReferenceList(ctx.list_partitions().column_reference_list()), specifiers);
 
     } else if (checkIfExist(ctx.column_partitions())) { // For Column Partition (Hive Style)
-      return new CreateTable.ColumnPartition(getDefinitions(ctx.column_partitions().table_elements()), true);
+      return new CreateTable.ColumnPartition(getDefinitions(ctx.column_partitions().table_elements()));
     } else {
       throw new SQLSyntaxError("Invalid Partition Type: " + ctx.toStringTree());
     }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
index 5811d36..8c3e606 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/AlgebraVisitor.java
@@ -91,13 +91,14 @@
   RESULT visitSign(CONTEXT ctx, Stack<Expr> stack, SignedExpr expr) throws PlanningException;
   RESULT visitColumnReference(CONTEXT ctx, Stack<Expr> stack, ColumnReferenceExpr expr) throws PlanningException;
   RESULT visitTargetExpr(CONTEXT ctx, Stack<Expr> stack, NamedExpr expr) throws PlanningException;
-  RESULT visitFunction(CONTEXT ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException;
   RESULT visitQualifiedAsterisk(CONTEXT ctx, Stack<Expr> stack, QualifiedAsteriskExpr expr) throws PlanningException;
 
-  // set functions
-  RESULT visitCountRowsFunction(CONTEXT ctx, Stack<Expr> stack, CountRowsFunctionExpr expr) throws PlanningException;
+  // functions
+  RESULT visitFunction(CONTEXT ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException;
   RESULT visitGeneralSetFunction(CONTEXT ctx, Stack<Expr> stack, GeneralSetFunctionExpr expr)
       throws PlanningException;
+  RESULT visitCountRowsFunction(CONTEXT ctx, Stack<Expr> stack, CountRowsFunctionExpr expr) throws PlanningException;
+  RESULT visitWindowFunction(CONTEXT ctx, Stack<Expr> stack, WindowFunctionExpr expr) throws PlanningException;
 
   // Literal
   RESULT visitCastExpr(CONTEXT ctx, Stack<Expr> stack, CastExpr expr) throws PlanningException;
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
index 907042a..24ff2e4 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BaseAlgebraVisitor.java
@@ -230,6 +230,10 @@
     case GeneralSetFunction:
       current = visitGeneralSetFunction(ctx, stack, (GeneralSetFunctionExpr) expr);
       break;
+    case WindowFunction:
+      current = visitWindowFunction(ctx, stack, (WindowFunctionExpr) expr);
+      break;
+
 
     case DataType:
       current = visitDataType(ctx, stack, (DataTypeExpr) expr);
@@ -703,6 +707,39 @@
     return result;
   }
 
+  @Override
+  public RESULT visitWindowFunction(CONTEXT ctx, Stack<Expr> stack, WindowFunctionExpr expr) throws PlanningException {
+    stack.push(expr);
+    RESULT result = null;
+    for (Expr param : expr.getParams()) {
+      result = visit(ctx, stack, param);
+    }
+
+    WindowSpec windowSpec = expr.getWindowSpec();
+
+    if (windowSpec.hasPartitionBy()) {
+      for (Expr partitionKey : windowSpec.getPartitionKeys()) {
+        visit(ctx, stack, partitionKey);
+      }
+    }
+    if (windowSpec.hasOrderBy()) {
+      for (Sort.SortSpec sortKey : windowSpec.getSortSpecs()) {
+        visit(ctx, stack, sortKey.getKey());
+      }
+    }
+    if (windowSpec.hasWindowFrame()) {
+      if (windowSpec.getWindowFrame().getStartBound().hasNumber()) {
+        visit(ctx, stack, windowSpec.getWindowFrame().getStartBound().getNumber());
+      }
+      if (windowSpec.getWindowFrame().getEndBound().hasNumber()) {
+        visit(ctx, stack, windowSpec.getWindowFrame().getEndBound().getNumber());
+      }
+    }
+
+    stack.pop();
+    return result;
+  }
+
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
   // Literal Section
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
index 2112615..a4e90b4 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/BasicLogicalPlanVisitor.java
@@ -74,6 +74,9 @@
       case GROUP_BY:
         current = visitGroupBy(context, plan, block, (GroupbyNode) node, stack);
         break;
+      case WINDOW_AGG:
+        current = visitWindowAgg(context, plan, block, (WindowAggNode) node, stack);
+        break;
       case DISTINCT_GROUP_BY:
         current = visitDistinct(context, plan, block, (DistinctGroupbyNode) node, stack);
         break;
@@ -191,6 +194,14 @@
   }
 
   @Override
+  public RESULT visitWindowAgg(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, WindowAggNode node,
+                               Stack<LogicalNode> stack) throws PlanningException {
+    stack.push(node);
+    RESULT result = visit(context, plan, block, node.getChild(), stack);
+    stack.pop();
+    return result;
+  }
+
   public RESULT visitDistinct(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, DistinctGroupbyNode node,
                              Stack<LogicalNode> stack) throws PlanningException {
     stack.push(node);
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
index ad9bdf1..a7e5375 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExplainLogicalPlanVisitor.java
@@ -109,6 +109,11 @@
   }
 
   @Override
+  public LogicalNode visitWindowAgg(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, WindowAggNode node,
+                                    Stack<LogicalNode> stack) throws PlanningException {
+    return visitUnaryNode(context, plan, block, node, stack);
+  }
+
   public LogicalNode visitDistinct(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, DistinctGroupbyNode node,
                                   Stack<LogicalNode> stack) throws PlanningException {
     return visitUnaryNode(context, plan, block, node, stack);
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
index e143823..2c386b2 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprAnnotator.java
@@ -19,13 +19,14 @@
 package org.apache.tajo.engine.planner;
 
 import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 import org.apache.tajo.algebra.*;
 import org.apache.tajo.catalog.CatalogService;
 import org.apache.tajo.catalog.CatalogUtil;
 import org.apache.tajo.catalog.Column;
 import org.apache.tajo.catalog.FunctionDesc;
 import org.apache.tajo.catalog.exception.NoSuchFunctionException;
-import org.apache.tajo.catalog.proto.CatalogProtos;
+import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.datum.*;
 import org.apache.tajo.engine.eval.*;
 import org.apache.tajo.engine.function.AggFunction;
@@ -38,10 +39,17 @@
 import org.apache.tajo.util.datetime.TimeMeta;
 
 import java.util.Map;
+import java.util.Set;
 import java.util.Stack;
 
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameEndBoundType;
+import static org.apache.tajo.algebra.WindowSpec.WindowFrameStartBoundType;
+import static org.apache.tajo.catalog.proto.CatalogProtos.FunctionType;
 import static org.apache.tajo.common.TajoDataTypes.DataType;
 import static org.apache.tajo.common.TajoDataTypes.Type;
+import static org.apache.tajo.engine.planner.logical.WindowSpec.WindowEndBound;
+import static org.apache.tajo.engine.planner.logical.WindowSpec.WindowFrame;
+import static org.apache.tajo.engine.planner.logical.WindowSpec.WindowStartBound;
 
 /**
  * <code>ExprAnnotator</code> makes an annotated expression called <code>EvalNode</code> from an
@@ -122,7 +130,7 @@
    * @return The widest DataType
    * @throws PlanningException when types are not compatible, it throws the exception.
    */
-  static DataType getWidestType(DataType...types) throws PlanningException {
+  public static DataType getWidestType(DataType...types) throws PlanningException {
     DataType widest = types[0];
     for (int i = 1; i < types.length; i++) {
 
@@ -541,6 +549,10 @@
     throw new PlanningException("ExprAnnotator cannot take NamedExpr");
   }
 
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Functions and General Set Functions Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
   @Override
   public EvalNode visitFunction(Context ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
     stack.push(expr); // <--- Push
@@ -589,18 +601,18 @@
 
 
     try {
-      CatalogProtos.FunctionType functionType = funcDesc.getFuncType();
-      if (functionType == CatalogProtos.FunctionType.GENERAL
-          || functionType == CatalogProtos.FunctionType.UDF) {
+      FunctionType functionType = funcDesc.getFuncType();
+      if (functionType == FunctionType.GENERAL
+          || functionType == FunctionType.UDF) {
         return new GeneralFunctionEval(funcDesc, (GeneralFunction) funcDesc.newInstance(), givenArgs);
-      } else if (functionType == CatalogProtos.FunctionType.AGGREGATION
-          || functionType == CatalogProtos.FunctionType.UDA) {
+      } else if (functionType == FunctionType.AGGREGATION
+          || functionType == FunctionType.UDA) {
         if (!ctx.currentBlock.hasNode(NodeType.GROUP_BY)) {
           ctx.currentBlock.setAggregationRequire();
         }
         return new AggregationFunctionCallEval(funcDesc, (AggFunction) funcDesc.newInstance(), givenArgs);
-      } else if (functionType == CatalogProtos.FunctionType.DISTINCT_AGGREGATION
-          || functionType == CatalogProtos.FunctionType.DISTINCT_UDA) {
+      } else if (functionType == FunctionType.DISTINCT_AGGREGATION
+          || functionType == FunctionType.DISTINCT_UDA) {
         throw new PlanningException("Unsupported function: " + funcDesc.toString());
       } else {
         throw new PlanningException("Unsupported Function Type: " + functionType.name());
@@ -610,14 +622,10 @@
     }
   }
 
-  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-  // General Set Section
-  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
   @Override
   public EvalNode visitCountRowsFunction(Context ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
       throws PlanningException {
-    FunctionDesc countRows = catalog.getFunction("count", CatalogProtos.FunctionType.AGGREGATION,
+    FunctionDesc countRows = catalog.getFunction("count", FunctionType.AGGREGATION,
         new DataType[] {});
     if (countRows == null) {
       throw new NoSuchFunctionException(expr.getSignature(), new DataType[]{});
@@ -641,8 +649,8 @@
     EvalNode[] givenArgs = new EvalNode[params.length];
     DataType[] paramTypes = new DataType[params.length];
 
-    CatalogProtos.FunctionType functionType = setFunction.isDistinct() ?
-        CatalogProtos.FunctionType.DISTINCT_AGGREGATION : CatalogProtos.FunctionType.AGGREGATION;
+    FunctionType functionType = setFunction.isDistinct() ?
+        FunctionType.DISTINCT_AGGREGATION : FunctionType.AGGREGATION;
     givenArgs[0] = visit(ctx, stack, params[0]);
     if (setFunction.getSignature().equalsIgnoreCase("count")) {
       paramTypes[0] = CatalogUtil.newSimpleDataType(Type.ANY);
@@ -666,6 +674,91 @@
     }
   }
 
+  public static final Set<String> WINDOW_FUNCTIONS =
+      Sets.newHashSet("row_number", "rank", "dense_rank", "percent_rank", "cume_dist");
+
+  public EvalNode visitWindowFunction(Context ctx, Stack<Expr> stack, WindowFunctionExpr windowFunc)
+      throws PlanningException {
+
+    WindowSpec windowSpec = windowFunc.getWindowSpec();
+
+    Expr key;
+    if (windowSpec.hasPartitionBy()) {
+      for (int i = 0; i < windowSpec.getPartitionKeys().length; i++) {
+        key = windowSpec.getPartitionKeys()[i];
+        visit(ctx, stack, key);
+      }
+    }
+
+    EvalNode [] sortKeys = null;
+    if (windowSpec.hasOrderBy()) {
+      sortKeys = new EvalNode[windowSpec.getSortSpecs().length];
+      for (int i = 0; i < windowSpec.getSortSpecs().length; i++) {
+        key = windowSpec.getSortSpecs()[i].getKey();
+        sortKeys[i] = visit(ctx, stack, key);
+      }
+    }
+
+    String funcName = windowFunc.getSignature();
+    boolean distinct = windowFunc.isDistinct();
+    Expr[] params = windowFunc.getParams();
+    EvalNode[] givenArgs = new EvalNode[params.length];
+    TajoDataTypes.DataType[] paramTypes = new TajoDataTypes.DataType[params.length];
+    FunctionType functionType;
+
+    WindowFrame frame = null;
+
+    if (params.length > 0) {
+      givenArgs[0] = visit(ctx, stack, params[0]);
+      if (windowFunc.getSignature().equalsIgnoreCase("count")) {
+        paramTypes[0] = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.ANY);
+      } else if (windowFunc.getSignature().equalsIgnoreCase("row_number")) {
+        paramTypes[0] = CatalogUtil.newSimpleDataType(Type.INT8);
+      } else {
+        paramTypes[0] = givenArgs[0].getValueType();
+      }
+    } else {
+      if (windowFunc.getSignature().equalsIgnoreCase("rank")) {
+        givenArgs = sortKeys != null ? sortKeys : new EvalNode[0];
+      }
+    }
+
+    if (frame == null) {
+      if (windowSpec.hasOrderBy()) {
+        frame = new WindowFrame(new WindowStartBound(WindowFrameStartBoundType.UNBOUNDED_PRECEDING),
+            new WindowEndBound(WindowFrameEndBoundType.CURRENT_ROW));
+      } else if (windowFunc.getSignature().equalsIgnoreCase("row_number")) {
+        frame = new WindowFrame(new WindowStartBound(WindowFrameStartBoundType.UNBOUNDED_PRECEDING),
+            new WindowEndBound(WindowFrameEndBoundType.UNBOUNDED_FOLLOWING));
+      } else {
+        frame = new WindowFrame();
+      }
+    }
+
+    // TODO - containFunction and getFunction should support the function type mask which provides ORing multiple types.
+    // the below checking against WINDOW_FUNCTIONS is a workaround code for the above problem.
+    if (WINDOW_FUNCTIONS.contains(funcName.toLowerCase())) {
+      if (distinct) {
+        throw new NoSuchFunctionException("row_number() does not support distinct keyword.");
+      }
+      functionType = FunctionType.WINDOW;
+    } else {
+      functionType = distinct ? FunctionType.DISTINCT_AGGREGATION : FunctionType.AGGREGATION;
+    }
+
+    if (!catalog.containFunction(windowFunc.getSignature(), functionType, paramTypes)) {
+      throw new NoSuchFunctionException(funcName, paramTypes);
+    }
+
+    FunctionDesc funcDesc = catalog.getFunction(funcName, functionType, paramTypes);
+
+    try {
+      return new WindowFunctionEval(funcDesc, (AggFunction) funcDesc.newInstance(), givenArgs, frame);
+    } catch (InternalException e) {
+      throw new PlanningException(e);
+    }
+  }
+
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
   // Literal Section
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
index 75b2b95..81bbd41 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/ExprNormalizer.java
@@ -18,12 +18,16 @@
 
 package org.apache.tajo.engine.planner;
 
+import com.google.common.collect.Sets;
+import com.google.common.collect.Sets;
 import org.apache.tajo.algebra.*;
 import org.apache.tajo.catalog.CatalogUtil;
 import org.apache.tajo.engine.exception.NoSuchColumnException;
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Set;
+import java.util.Set;
 import java.util.Stack;
 
 /**
@@ -87,6 +91,8 @@
                    // function.
     List<NamedExpr> aggExprs = new ArrayList<NamedExpr>(); // aggregation functions
     List<NamedExpr> scalarExprs = new ArrayList<NamedExpr>(); // scalar expressions which can be referred
+    List<NamedExpr> windowAggExprs = new ArrayList<NamedExpr>(); // window expressions which can be referred
+    Set<WindowSpecReferences> windowSpecs = Sets.newLinkedHashSet();
 
     private ExprNormalizedResult(LogicalPlanner.PlanContext context, boolean tryBinaryCommonTermsElimination) {
       this.plan = context.plan;
@@ -248,7 +254,7 @@
 
       // If parameters are all constants, we don't need to dissect an aggregation expression into two parts:
       // function and parameter parts.
-      if (!OpType.isLiteral(param.getType()) && param.getType() != OpType.Column) {
+      if (!OpType.isLiteralType(param.getType()) && param.getType() != OpType.Column) {
         String referenceName = ctx.block.namedExprsMgr.addExpr(param);
         ctx.scalarExprs.add(new NamedExpr(param, referenceName));
         expr.getParams()[i] = new ColumnReferenceExpr(referenceName);
@@ -258,6 +264,54 @@
     return expr;
   }
 
+  public Expr visitWindowFunction(ExprNormalizedResult ctx, Stack<Expr> stack, WindowFunctionExpr expr)
+      throws PlanningException {
+    stack.push(expr);
+
+    WindowSpec windowSpec = expr.getWindowSpec();
+    Expr key;
+
+    WindowSpecReferences windowSpecReferences;
+    if (windowSpec.hasWindowName()) {
+      windowSpecReferences = new WindowSpecReferences(windowSpec.getWindowName());
+    } else {
+      String [] partitionKeyReferenceNames = null;
+      if (windowSpec.hasPartitionBy()) {
+        partitionKeyReferenceNames = new String [windowSpec.getPartitionKeys().length];
+        for (int i = 0; i < windowSpec.getPartitionKeys().length; i++) {
+          key = windowSpec.getPartitionKeys()[i];
+          visit(ctx, stack, key);
+          partitionKeyReferenceNames[i] = ctx.block.namedExprsMgr.addExpr(key);
+        }
+      }
+
+      String [] orderKeyReferenceNames = null;
+      if (windowSpec.hasOrderBy()) {
+        orderKeyReferenceNames = new String[windowSpec.getSortSpecs().length];
+        for (int i = 0; i < windowSpec.getSortSpecs().length; i++) {
+          key = windowSpec.getSortSpecs()[i].getKey();
+          visit(ctx, stack, key);
+          String referenceName = ctx.block.namedExprsMgr.addExpr(key);
+          if (OpType.isAggregationFunction(key.getType())) {
+            ctx.aggExprs.add(new NamedExpr(key, referenceName));
+            windowSpec.getSortSpecs()[i].setKey(new ColumnReferenceExpr(referenceName));
+          }
+          orderKeyReferenceNames[i] = referenceName;
+        }
+      }
+      windowSpecReferences =
+          new WindowSpecReferences(partitionKeyReferenceNames,orderKeyReferenceNames);
+    }
+    ctx.windowSpecs.add(windowSpecReferences);
+
+    String funcExprRef = ctx.block.namedExprsMgr.addExpr(expr);
+    ctx.windowAggExprs.add(new NamedExpr(expr, funcExprRef));
+    stack.pop();
+
+    ctx.block.setHasWindowFunction();
+    return expr;
+  }
+
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
   // Literal Section
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -288,4 +342,40 @@
     }
     return expr;
   }
+
+  public static class WindowSpecReferences {
+    String windowName;
+
+    String [] partitionKeys;
+    String [] orderKeys;
+
+    public WindowSpecReferences(String windowName) {
+      this.windowName = windowName;
+    }
+
+    public WindowSpecReferences(String [] partitionKeys, String [] orderKeys) {
+      this.partitionKeys = partitionKeys;
+      this.orderKeys = orderKeys;
+    }
+
+    public String getWindowName() {
+      return windowName;
+    }
+
+    public boolean hasPartitionKeys() {
+      return partitionKeys != null;
+    }
+
+    public String [] getPartitionKeys() {
+      return partitionKeys;
+    }
+
+    public boolean hasOrderBy() {
+      return orderKeys != null;
+    }
+
+    public String [] getOrderKeys() {
+      return orderKeys;
+    }
+  }
 }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
index 92df760..4e1d313 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlan.java
@@ -19,6 +19,7 @@
 package org.apache.tajo.engine.planner;
 
 import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
 import org.apache.commons.lang.ObjectUtils;
 import org.apache.tajo.algebra.*;
 import org.apache.tajo.annotation.NotThreadSafe;
@@ -48,6 +49,8 @@
   /** the prefix character for virtual tables */
   public static final char VIRTUAL_TABLE_PREFIX='#';
   public static final char NONAMED_COLUMN_PREFIX='?';
+  public static final char NONAMED_WINDOW_PREFIX='^';
+
   /** it indicates the root block */
   public static final String ROOT_BLOCK = VIRTUAL_TABLE_PREFIX + "ROOT";
   public static final String NONAME_BLOCK_PREFIX = VIRTUAL_TABLE_PREFIX + "QB_";
@@ -55,6 +58,7 @@
   private int nextPid = 0;
   private Integer noNameBlockId = 0;
   private Integer noNameColumnId = 0;
+  private Integer noNameWindowId = 0;
 
   /** a map from between a block name to a block plan */
   private Map<String, QueryBlock> queryBlocks = new LinkedHashMap<String, QueryBlock>();
@@ -134,6 +138,10 @@
     return newAndGetBlock(NONAME_BLOCK_PREFIX + (noNameBlockId++));
   }
 
+  public void resetGeneratedId() {
+    noNameColumnId = 0;
+  }
+
   /**
    * It generates an unique column name from EvalNode. It is usually used for an expression or predicate without
    * a specified name (i.e., alias).
@@ -596,6 +604,8 @@
     private final Map<String, List<String>> aliasMap = TUtil.newHashMap();
     private final Map<OpType, List<Expr>> operatorToExprMap = TUtil.newHashMap();
     private final List<RelationNode> relationList = TUtil.newList();
+    private boolean hasWindowFunction = false;
+
     /**
      * It's a map between nodetype and node. node types can be duplicated. So, latest node type is only kept.
      */
@@ -779,6 +789,14 @@
       return (T) exprToNodeMap.get(ObjectUtils.identityToString(expr));
     }
 
+    public void setHasWindowFunction() {
+      hasWindowFunction = true;
+    }
+
+    public boolean hasWindowSpecs() {
+      return hasWindowFunction;
+    }
+
     /**
      * This flag can be changed as a plan is generated.
      *
@@ -823,4 +841,4 @@
       return blockName;
     }
   }
-}
\ No newline at end of file
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
index 2de96c4..4f1218f 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanPreprocessor.java
@@ -20,8 +20,8 @@
 
 import org.apache.tajo.algebra.*;
 import org.apache.tajo.catalog.*;
+import org.apache.tajo.common.TajoDataTypes;
 import org.apache.tajo.engine.eval.EvalNode;
-import org.apache.tajo.engine.eval.EvalType;
 import org.apache.tajo.engine.eval.FieldEval;
 import org.apache.tajo.engine.exception.NoSuchColumnException;
 import org.apache.tajo.engine.planner.LogicalPlan.QueryBlock;
@@ -35,13 +35,14 @@
 /**
  * It finds all relations for each block and builds base schema information.
  */
-class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanPreprocessor.PreprocessContext, LogicalNode> {
+public class LogicalPlanPreprocessor extends BaseAlgebraVisitor<LogicalPlanPreprocessor.PreprocessContext, LogicalNode> {
+  private TypeDeterminant typeDeterminant;
   private ExprAnnotator annotator;
 
-  static class PreprocessContext {
-    Session session;
-    LogicalPlan plan;
-    LogicalPlan.QueryBlock currentBlock;
+  public static class PreprocessContext {
+    public Session session;
+    public LogicalPlan plan;
+    public LogicalPlan.QueryBlock currentBlock;
 
     public PreprocessContext(Session session, LogicalPlan plan, LogicalPlan.QueryBlock currentBlock) {
       this.session = session;
@@ -62,6 +63,7 @@
   LogicalPlanPreprocessor(CatalogService catalog, ExprAnnotator annotator) {
     this.catalog = catalog;
     this.annotator = annotator;
+    this.typeDeterminant = new TypeDeterminant(catalog);
   }
 
   @Override
@@ -201,16 +203,13 @@
 
     for (int i = 0; i < expr.getNamedExprs().length; i++) {
       NamedExpr namedExpr = expr.getNamedExprs()[i];
-      EvalNode evalNode = annotator.createEvalNode(ctx.plan, ctx.currentBlock, namedExpr.getExpr());
+      TajoDataTypes.DataType dataType = typeDeterminant.determineDataType(ctx, namedExpr.getExpr());
 
       if (namedExpr.hasAlias()) {
-        targets[i] = new Target(evalNode, namedExpr.getAlias());
-      } else if (evalNode.getType() == EvalType.FIELD) {
-        targets[i] = new Target((FieldEval) evalNode);
+        targets[i] = new Target(new FieldEval(new Column(namedExpr.getAlias(), dataType)));
       } else {
         String generatedName = ctx.plan.generateUniqueColumnName(namedExpr.getExpr());
-        targets[i] = new Target(evalNode, generatedName);
-        namedExpr.setAlias(generatedName);
+        targets[i] = new Target(new FieldEval(new Column(generatedName, dataType)));
       }
     }
     stack.pop(); // <--- Pop
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanVisitor.java
index 963e9f1..0a36610 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanVisitor.java
@@ -40,7 +40,8 @@
 
   RESULT visitGroupBy(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node,
                       Stack<LogicalNode> stack) throws PlanningException;
-
+  RESULT visitWindowAgg(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, WindowAggNode node,
+                      Stack<LogicalNode> stack) throws PlanningException;
   RESULT visitDistinct(CONTEXT context, LogicalPlan plan, LogicalPlan.QueryBlock block, DistinctGroupbyNode node,
                                 Stack<LogicalNode> stack) throws PlanningException;
 
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
index be7bce6..80390d3 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/LogicalPlanner.java
@@ -30,6 +30,7 @@
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.tajo.algebra.*;
+import org.apache.tajo.algebra.WindowSpec;
 import org.apache.tajo.catalog.*;
 import org.apache.tajo.catalog.partition.PartitionMethodDesc;
 import org.apache.tajo.catalog.proto.CatalogProtos;
@@ -43,6 +44,7 @@
 import org.apache.tajo.engine.utils.SchemaUtil;
 import org.apache.tajo.master.session.Session;
 import org.apache.tajo.storage.StorageUtil;
+import org.apache.tajo.util.Pair;
 import org.apache.tajo.util.KeyValueSet;
 import org.apache.tajo.util.TUtil;
 
@@ -117,6 +119,7 @@
     QueryBlock rootBlock = plan.newAndGetBlock(LogicalPlan.ROOT_BLOCK);
     PreprocessContext preProcessorCtx = new PreprocessContext(session, plan, rootBlock);
     preprocessor.visit(preProcessorCtx, new Stack<Expr>(), expr);
+    plan.resetGeneratedId();
 
     PlanContext context = new PlanContext(session, plan, plan.getRootBlock(), debug);
     LogicalNode topMostNode = this.visit(context, new Stack<Expr>(), expr);
@@ -196,7 +199,8 @@
     String [] referenceNames;
     // in prephase, insert all target list into NamedExprManagers.
     // Then it gets reference names, each of which points an expression in target list.
-    referenceNames = doProjectionPrephase(context, projection);
+    Pair<String [], ExprNormalizer.WindowSpecReferences []> referencesPair = doProjectionPrephase(context, projection);
+    referenceNames = referencesPair.getFirst();
 
     ////////////////////////////////////////////////////////
     // Visit and Build Child Plan
@@ -208,6 +212,14 @@
     if (block.isAggregationRequired()) {
       child = insertGroupbyNode(context, child, stack);
     }
+
+    if (block.hasWindowSpecs()) {
+      LogicalNode windowAggNode =
+          insertWindowAggNode(context, child, stack, referenceNames, referencesPair.getSecond());
+      if (windowAggNode != null) {
+        child = windowAggNode;
+      }
+    }
     stack.pop();
     ////////////////////////////////////////////////////////
 
@@ -274,30 +286,73 @@
     projectionNode.setInSchema(dupRemoval.getOutSchema());
   }
 
-  private String [] doProjectionPrephase(PlanContext context, Projection projection) throws PlanningException {
+  private Pair<String [], ExprNormalizer.WindowSpecReferences []> doProjectionPrephase(PlanContext context,
+                                                                                    Projection projection)
+      throws PlanningException {
+
     QueryBlock block = context.queryBlock;
 
     int finalTargetNum = projection.size();
     String [] referenceNames = new String[finalTargetNum];
     ExprNormalizedResult [] normalizedExprList = new ExprNormalizedResult[finalTargetNum];
-    NamedExpr namedExpr;
-    for (int i = 0; i < finalTargetNum; i++) {
-      namedExpr = projection.getNamedExprs()[i];
 
-      if (PlannerUtil.existsAggregationFunction(namedExpr)) {
-        block.setAggregationRequire();
+    List<ExprNormalizer.WindowSpecReferences> windowSpecReferencesList = TUtil.newList();
+
+    List<Integer> targetsIds = normalize(context, projection, normalizedExprList, new Matcher() {
+      @Override
+      public boolean isMatch(Expr expr) {
+        return ExprFinder.finds(expr, OpType.WindowFunction).size() == 0;
       }
-      // dissect an expression into multiple parts (at most dissected into three parts)
-      normalizedExprList[i] = normalizer.normalize(context, namedExpr.getExpr());
-    }
+    });
 
     // Note: Why separate normalization and add(Named)Expr?
     //
     // ExprNormalizer internally makes use of the named exprs in NamedExprsManager.
     // If we don't separate normalization work and addExprWithName, addExprWithName will find named exprs evaluated
     // the same logical node. It will cause impossible evaluation in physical executors.
-    for (int i = 0; i < finalTargetNum; i++) {
-      namedExpr = projection.getNamedExprs()[i];
+    addNamedExprs(block, referenceNames, normalizedExprList, windowSpecReferencesList, projection, targetsIds);
+
+    targetsIds = normalize(context, projection, normalizedExprList, new Matcher() {
+      @Override
+      public boolean isMatch(Expr expr) {
+        return ExprFinder.finds(expr, OpType.WindowFunction).size() > 0;
+      }
+    });
+    addNamedExprs(block, referenceNames, normalizedExprList, windowSpecReferencesList, projection, targetsIds);
+
+    return new Pair<String[], ExprNormalizer.WindowSpecReferences []>(referenceNames,
+        windowSpecReferencesList.toArray(new ExprNormalizer.WindowSpecReferences[windowSpecReferencesList.size()]));
+  }
+
+  private interface Matcher {
+    public boolean isMatch(Expr expr);
+  }
+
+  public List<Integer> normalize(PlanContext context, Projection projection, ExprNormalizedResult [] normalizedExprList,
+                                 Matcher matcher) throws PlanningException {
+    List<Integer> targetIds = new ArrayList<Integer>();
+    for (int i = 0; i < projection.size(); i++) {
+      NamedExpr namedExpr = projection.getNamedExprs()[i];
+
+      if (PlannerUtil.existsAggregationFunction(namedExpr)) {
+        context.queryBlock.setAggregationRequire();
+      }
+
+      if (matcher.isMatch(namedExpr.getExpr())) {
+        // dissect an expression into multiple parts (at most dissected into three parts)
+        normalizedExprList[i] = normalizer.normalize(context, namedExpr.getExpr());
+        targetIds.add(i);
+      }
+    }
+
+    return targetIds;
+  }
+
+  private void addNamedExprs(QueryBlock block, String [] referenceNames, ExprNormalizedResult [] normalizedExprList,
+                             List<ExprNormalizer.WindowSpecReferences> windowSpecReferencesList, Projection projection,
+                             List<Integer> targetIds) throws PlanningException {
+    for (int i : targetIds) {
+      NamedExpr namedExpr = projection.getNamedExprs()[i];
       // Get all projecting references
       if (namedExpr.hasAlias()) {
         NamedExpr aliasedExpr = new NamedExpr(normalizedExprList[i].baseExpr, namedExpr.getAlias());
@@ -309,9 +364,10 @@
       // Add sub-expressions (i.e., aggregation part and scalar part) from dissected parts.
       block.namedExprsMgr.addNamedExprArray(normalizedExprList[i].aggExprs);
       block.namedExprsMgr.addNamedExprArray(normalizedExprList[i].scalarExprs);
-    }
+      block.namedExprsMgr.addNamedExprArray(normalizedExprList[i].windowAggExprs);
 
-    return referenceNames;
+      windowSpecReferencesList.addAll(normalizedExprList[i].windowSpecs);
+    }
   }
 
   /**
@@ -359,64 +415,246 @@
     return targets;
   }
 
+  /**
+   * It checks if all targets of Projectable plan node can be evaluated from the child node.
+   * It can avoid potential errors which possibly occur in physical operators.
+   *
+   * @param block QueryBlock which includes the Projectable node
+   * @param projectable Projectable node to be valid
+   * @throws PlanningException
+   */
   public static void verifyProjectedFields(QueryBlock block, Projectable projectable) throws PlanningException {
-    if (projectable instanceof ProjectionNode && block.hasNode(NodeType.GROUP_BY)) {
-      for (Target target : projectable.getTargets()) {
-        Set<Column> columns = EvalTreeUtil.findUniqueColumns(target.getEvalTree());
-        for (Column c : columns) {
-          if (!projectable.getInSchema().contains(c)) {
-            throw new PlanningException(c.getQualifiedName()
-                + " must appear in the GROUP BY clause or be used in an aggregate function at node ("
-                + projectable.getPID() + ")" );
-          }
-        }
-      }
-    } else  if (projectable instanceof GroupbyNode) {
+    if (projectable instanceof GroupbyNode) {
       GroupbyNode groupbyNode = (GroupbyNode) projectable;
-      // It checks if all column references within each target can be evaluated with the input schema.
-      int groupingColumnNum = groupbyNode.getGroupingColumns().length;
-      for (int i = 0; i < groupingColumnNum; i++) {
-        Set<Column> columns = EvalTreeUtil.findUniqueColumns(groupbyNode.getTargets()[i].getEvalTree());
-        if (!projectable.getInSchema().containsAll(columns)) {
-          throw new PlanningException(String.format("Cannot get the field(s) \"%s\" at node (%d)",
-              TUtil.collectionToString(columns), projectable.getPID()));
-        }
-      }
-      if (groupbyNode.hasAggFunctions()) {
-        for (AggregationFunctionCallEval f : groupbyNode.getAggFunctions()) {
-          Set<Column> columns = EvalTreeUtil.findUniqueColumns(f);
-          for (Column c : columns) {
-            if (!projectable.getInSchema().contains(c)) {
-              throw new PlanningException(String.format("Cannot get the field \"%s\" at node (%d)",
-                  c, projectable.getPID()));
+
+      if (!groupbyNode.isEmptyGrouping()) { // it should be targets instead of
+        int groupingKeyNum = groupbyNode.getGroupingColumns().length;
+
+        for (int i = 0; i < groupingKeyNum; i++) {
+          Target target = groupbyNode.getTargets()[i];
+          if (groupbyNode.getTargets()[i].getEvalTree().getType() == EvalType.FIELD) {
+            FieldEval grpKeyEvalNode = target.getEvalTree();
+            if (!groupbyNode.getInSchema().contains(grpKeyEvalNode.getColumnRef())) {
+              throwCannotEvaluateException(projectable, grpKeyEvalNode.getName());
             }
           }
         }
       }
+
+      if (groupbyNode.hasAggFunctions()) {
+        verifyIfEvalNodesCanBeEvaluated(projectable, groupbyNode.getAggFunctions());
+      }
+
+    } else if (projectable instanceof WindowAggNode) {
+      WindowAggNode windowAggNode = (WindowAggNode) projectable;
+
+      if (windowAggNode.hasPartitionKeys()) {
+        verifyIfColumnCanBeEvaluated(projectable.getInSchema(), projectable, windowAggNode.getPartitionKeys());
+      }
+
+      if (windowAggNode.hasAggFunctions()) {
+        verifyIfEvalNodesCanBeEvaluated(projectable, windowAggNode.getWindowFunctions());
+      }
+
+      if (windowAggNode.hasSortSpecs()) {
+        Column [] sortKeys = PlannerUtil.sortSpecsToSchema(windowAggNode.getSortSpecs()).toArray();
+        verifyIfColumnCanBeEvaluated(projectable.getInSchema(), projectable, sortKeys);
+      }
+
+      // verify targets except for function slots
+      for (int i = 0; i < windowAggNode.getTargets().length - windowAggNode.getWindowFunctions().length; i++) {
+        Target target = windowAggNode.getTargets()[i];
+        Set<Column> columns = EvalTreeUtil.findUniqueColumns(target.getEvalTree());
+        for (Column c : columns) {
+          if (!windowAggNode.getInSchema().contains(c)) {
+            throwCannotEvaluateException(projectable, c.getQualifiedName());
+          }
+        }
+      }
+
     } else if (projectable instanceof RelationNode) {
       RelationNode relationNode = (RelationNode) projectable;
-      for (Target target : projectable.getTargets()) {
-        Set<Column> columns = EvalTreeUtil.findUniqueColumns(target.getEvalTree());
-        for (Column c : columns) {
-          if (!relationNode.getTableSchema().contains(c)) {
-            throw new PlanningException(String.format("Cannot get the field \"%s\" at node (%d)",
-                c, projectable.getPID()));
-          }
-        }
-      }
+      verifyIfTargetsCanBeEvaluated(relationNode.getTableSchema(), (Projectable) relationNode);
+
     } else {
-      for (Target target : projectable.getTargets()) {
-        Set<Column> columns = EvalTreeUtil.findUniqueColumns(target.getEvalTree());
-        for (Column c : columns) {
-          if (!projectable.getInSchema().contains(c)) {
-            throw new PlanningException(String.format("Cannot get the field \"%s\" at node (%d)",
-                c, projectable.getPID()));
-          }
+      verifyIfTargetsCanBeEvaluated(projectable.getInSchema(), projectable);
+    }
+  }
+
+  public static void verifyIfEvalNodesCanBeEvaluated(Projectable projectable, EvalNode[] evalNodes)
+      throws PlanningException {
+    for (EvalNode e : evalNodes) {
+      Set<Column> columns = EvalTreeUtil.findUniqueColumns(e);
+      for (Column c : columns) {
+        if (!projectable.getInSchema().contains(c)) {
+          throwCannotEvaluateException(projectable, c.getQualifiedName());
         }
       }
     }
   }
 
+  public static void verifyIfTargetsCanBeEvaluated(Schema baseSchema, Projectable projectable)
+      throws PlanningException {
+    for (Target target : projectable.getTargets()) {
+      Set<Column> columns = EvalTreeUtil.findUniqueColumns(target.getEvalTree());
+      for (Column c : columns) {
+        if (!baseSchema.contains(c)) {
+          throwCannotEvaluateException(projectable, c.getQualifiedName());
+        }
+      }
+    }
+  }
+
+  public static void verifyIfColumnCanBeEvaluated(Schema baseSchema, Projectable projectable, Column [] columns)
+      throws PlanningException {
+    for (Column c : columns) {
+      if (!baseSchema.contains(c)) {
+        throwCannotEvaluateException(projectable, c.getQualifiedName());
+      }
+    }
+  }
+
+  public static void throwCannotEvaluateException(Projectable projectable, String columnName) throws PlanningException {
+    if (projectable instanceof UnaryNode && ((UnaryNode) projectable).getChild().getType() == NodeType.GROUP_BY) {
+      throw new PlanningException(columnName
+          + " must appear in the GROUP BY clause or be used in an aggregate function at node ("
+          + projectable.getPID() + ")");
+    } else {
+      throw new PlanningException(String.format("Cannot evaluate the field \"%s\" at node (%d)",
+          columnName, projectable.getPID()));
+    }
+  }
+
+  private LogicalNode insertWindowAggNode(PlanContext context, LogicalNode child, Stack<Expr> stack,
+                                          String [] referenceNames,
+                                          ExprNormalizer.WindowSpecReferences [] windowSpecReferenceses)
+      throws PlanningException {
+    LogicalPlan plan = context.plan;
+    QueryBlock block = context.queryBlock;
+    WindowAggNode windowAggNode = context.plan.createNode(WindowAggNode.class);
+    if (child.getType() == NodeType.LIMIT) {
+      LimitNode limitNode = (LimitNode) child;
+      windowAggNode.setChild(limitNode.getChild());
+      windowAggNode.setInSchema(limitNode.getChild().getOutSchema());
+      limitNode.setChild(windowAggNode);
+    } else if (child.getType() == NodeType.SORT) {
+      SortNode sortNode = (SortNode) child;
+      windowAggNode.setChild(sortNode.getChild());
+      windowAggNode.setInSchema(sortNode.getChild().getOutSchema());
+      sortNode.setChild(windowAggNode);
+    } else {
+      windowAggNode.setChild(child);
+      windowAggNode.setInSchema(child.getOutSchema());
+    }
+
+    List<String> winFuncRefs = new ArrayList<String>();
+    List<WindowFunctionEval> winFuncs = new ArrayList<WindowFunctionEval>();
+    List<WindowSpec> rawWindowSpecs = Lists.newArrayList();
+    for (Iterator<NamedExpr> it = block.namedExprsMgr.getIteratorForUnevaluatedExprs(); it.hasNext();) {
+      NamedExpr rawTarget = it.next();
+      try {
+        EvalNode evalNode = exprAnnotator.createEvalNode(context.plan, context.queryBlock, rawTarget.getExpr());
+        if (evalNode.getType() == EvalType.WINDOW_FUNCTION) {
+          winFuncRefs.add(rawTarget.getAlias());
+          winFuncs.add((WindowFunctionEval) evalNode);
+          block.namedExprsMgr.markAsEvaluated(rawTarget.getAlias(), evalNode);
+
+          // TODO - Later, we also consider the possibility that a window function contains only a window name.
+          rawWindowSpecs.add(((WindowFunctionExpr) (rawTarget.getExpr())).getWindowSpec());
+        }
+      } catch (VerifyException ve) {
+      }
+    }
+
+    // we only consider one window definition.
+    if (windowSpecReferenceses[0].hasPartitionKeys()) {
+      Column [] partitionKeyColumns = new Column[windowSpecReferenceses[0].getPartitionKeys().length];
+      int i = 0;
+      for (String partitionKey : windowSpecReferenceses[0].getPartitionKeys()) {
+        if (block.namedExprsMgr.isEvaluated(partitionKey)) {
+          partitionKeyColumns[i++] = block.namedExprsMgr.getTarget(partitionKey).getNamedColumn();
+        } else {
+          throw new PlanningException("Each grouping column expression must be a scalar expression.");
+        }
+      }
+      windowAggNode.setPartitionKeys(partitionKeyColumns);
+    }
+
+    SortSpec [][] sortGroups = new SortSpec[rawWindowSpecs.size()][];
+
+    for (int winSpecIdx = 0; winSpecIdx < rawWindowSpecs.size(); winSpecIdx++) {
+      WindowSpec spec = rawWindowSpecs.get(winSpecIdx);
+      if (spec.hasOrderBy()) {
+        Sort.SortSpec [] sortSpecs = spec.getSortSpecs();
+        int sortNum = sortSpecs.length;
+        String [] sortKeyRefNames = windowSpecReferenceses[winSpecIdx].getOrderKeys();
+        SortSpec [] annotatedSortSpecs = new SortSpec[sortNum];
+
+        Column column;
+        for (int i = 0; i < sortNum; i++) {
+          if (block.namedExprsMgr.isEvaluated(sortKeyRefNames[i])) {
+            column = block.namedExprsMgr.getTarget(sortKeyRefNames[i]).getNamedColumn();
+          } else {
+            throw new IllegalStateException("Unexpected State: " + TUtil.arrayToString(sortSpecs));
+          }
+          annotatedSortSpecs[i] = new SortSpec(column, sortSpecs[i].isAscending(), sortSpecs[i].isNullFirst());
+        }
+
+        sortGroups[winSpecIdx] = annotatedSortSpecs;
+      } else {
+        sortGroups[winSpecIdx] = null;
+      }
+    }
+
+    for (int i = 0; i < winFuncRefs.size(); i++) {
+      WindowFunctionEval winFunc = winFuncs.get(i);
+      if (sortGroups[i] != null) {
+        winFunc.setSortSpecs(sortGroups[i]);
+      }
+    }
+
+    Target [] targets = new Target[referenceNames.length];
+    List<Integer> windowFuncIndices = Lists.newArrayList();
+    Projection projection = (Projection) stack.peek();
+    int windowFuncIdx = 0;
+    for (NamedExpr expr : projection.getNamedExprs()) {
+      if (expr.getExpr().getType() == OpType.WindowFunction) {
+        windowFuncIndices.add(windowFuncIdx);
+      }
+      windowFuncIdx++;
+    }
+    windowAggNode.setWindowFunctions(winFuncs.toArray(new WindowFunctionEval[winFuncs.size()]));
+
+    int targetIdx = 0;
+    for (int i = 0; i < referenceNames.length ; i++) {
+      if (!windowFuncIndices.contains(i)) {
+        targets[targetIdx++] = block.namedExprsMgr.getTarget(referenceNames[i]);
+      }
+    }
+    for (int i = 0; i < winFuncRefs.size(); i++) {
+      targets[targetIdx++] = block.namedExprsMgr.getTarget(winFuncRefs.get(i));
+    }
+    windowAggNode.setTargets(targets);
+    verifyProjectedFields(block, windowAggNode);
+
+    block.registerNode(windowAggNode);
+    postHook(context, stack, null, windowAggNode);
+
+    if (child.getType() == NodeType.LIMIT) {
+      LimitNode limitNode = (LimitNode) child;
+      limitNode.setInSchema(windowAggNode.getOutSchema());
+      limitNode.setOutSchema(windowAggNode.getOutSchema());
+      return null;
+    } else if (child.getType() == NodeType.SORT) {
+      SortNode sortNode = (SortNode) child;
+      sortNode.setInSchema(windowAggNode.getOutSchema());
+      sortNode.setOutSchema(windowAggNode.getOutSchema());
+      return null;
+    } else {
+      return windowAggNode;
+    }
+  }
+
   /**
    * Insert a group-by operator before a sort or a projection operator.
    * It is used only when a group-by clause is not given.
@@ -458,6 +696,8 @@
     // this inserted group-by node doesn't pass through preprocessor. So manually added.
     block.registerNode(groupbyNode);
     postHook(context, stack, null, groupbyNode);
+
+    verifyProjectedFields(block, groupbyNode);
     return groupbyNode;
   }
 
@@ -1541,7 +1781,7 @@
     return new Column(columnDefinition.getColumnName(), convertDataType(columnDefinition));
   }
 
-  static TajoDataTypes.DataType convertDataType(DataTypeExpr dataType) {
+  public static TajoDataTypes.DataType convertDataType(DataTypeExpr dataType) {
     TajoDataTypes.Type type = TajoDataTypes.Type.valueOf(dataType.getTypeName());
 
     TajoDataTypes.DataType.Builder builder = TajoDataTypes.DataType.newBuilder();
@@ -1600,6 +1840,20 @@
     Util SECTION
   ===============================================================================================*/
 
+  public static boolean checkIfBeEvaluatedAtWindowAgg(EvalNode evalNode, WindowAggNode node) {
+    Set<Column> columnRefs = EvalTreeUtil.findUniqueColumns(evalNode);
+
+    if (columnRefs.size() > 0 && !node.getInSchema().containsAll(columnRefs)) {
+      return false;
+    }
+
+    if (EvalTreeUtil.findDistinctAggFunction(evalNode).size() > 0) {
+      return false;
+    }
+
+    return true;
+  }
+
   public static boolean checkIfBeEvaluatedAtGroupBy(EvalNode evalNode, GroupbyNode node) {
     Set<Column> columnRefs = EvalTreeUtil.findUniqueColumns(evalNode);
 
@@ -1607,6 +1861,10 @@
       return false;
     }
 
+    if (EvalTreeUtil.findEvalsByType(evalNode, EvalType.WINDOW_FUNCTION).size() > 0) {
+      return false;
+    }
+
     return true;
   }
 
@@ -1618,6 +1876,10 @@
       return false;
     }
 
+    if (EvalTreeUtil.findEvalsByType(evalNode, EvalType.WINDOW_FUNCTION).size() > 0) {
+      return false;
+    }
+
     if (columnRefs.size() > 0 && !node.getInSchema().containsAll(columnRefs)) {
       return false;
     }
@@ -1658,6 +1920,11 @@
       return false;
     }
 
+    // aggregation functions cannot be evaluated in scan node
+    if (EvalTreeUtil.findEvalsByType(evalNode, EvalType.WINDOW_FUNCTION).size() > 0) {
+      return false;
+    }
+
     if (columnRefs.size() > 0 && !node.getTableSchema().containsAll(columnRefs)) {
       return false;
     }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
index f41d61d..7b643c3 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/PhysicalPlannerImpl.java
@@ -163,7 +163,6 @@
         leftExec = createPlanRecursive(ctx, subQueryNode.getSubQuery(), stack);
         stack.pop();
         return new ProjectionExec(ctx, subQueryNode, leftExec);
-
       }
 
       case PARTITIONS_SCAN:
@@ -178,6 +177,13 @@
         stack.pop();
         return createGroupByPlan(ctx, grpNode, leftExec);
 
+      case WINDOW_AGG:
+        WindowAggNode windowAggNode = (WindowAggNode) logicalNode;
+        stack.push(windowAggNode);
+        leftExec = createPlanRecursive(ctx, windowAggNode.getChild(), stack);
+        stack.pop();
+        return createWindowAgg(ctx, windowAggNode, leftExec);
+
       case DISTINCT_GROUP_BY:
         DistinctGroupbyNode distinctNode = (DistinctGroupbyNode) logicalNode;
         stack.push(distinctNode);
@@ -973,6 +979,27 @@
     }
   }
 
+  public PhysicalExec createWindowAgg(TaskAttemptContext context,WindowAggNode windowAggNode, PhysicalExec subOp)
+      throws IOException {
+    PhysicalExec child = subOp;
+    if (windowAggNode.hasPartitionKeys()) {
+      Column[] grpColumns = windowAggNode.getPartitionKeys();
+      SortSpec[] sortSpecs = new SortSpec[grpColumns.length];
+      for (int i = 0; i < grpColumns.length; i++) {
+        sortSpecs[i] = new SortSpec(grpColumns[i], true, false);
+      }
+
+      SortNode sortNode = LogicalPlan.createNodeWithoutPID(SortNode.class);
+      sortNode.setSortSpecs(sortSpecs);
+      sortNode.setInSchema(subOp.getSchema());
+      sortNode.setOutSchema(subOp.getSchema());
+      child = new ExternalSortExec(context, sm, sortNode, subOp);
+      LOG.info("The planner chooses [Sort Aggregation] in (" + TUtil.arrayToString(sortSpecs) + ")");
+    }
+
+    return new WindowAggExec(context, windowAggNode, child);
+  }
+
   public PhysicalExec createDistinctGroupByPlan(TaskAttemptContext context,
                                                 DistinctGroupbyNode distinctNode, PhysicalExec subOp)
       throws IOException {
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
index 1d8fd0f..55892ee 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/PlannerUtil.java
@@ -499,10 +499,10 @@
   }
 
   public static SortSpec[] schemaToSortSpecs(Schema schema) {
-    return schemaToSortSpecs(schema.toArray());
+    return columnsToSortSpecs(schema.toArray());
   }
 
-  public static SortSpec[] schemaToSortSpecs(Column[] columns) {
+  public static SortSpec[] columnsToSortSpecs(Column[] columns) {
     SortSpec[] specs = new SortSpec[columns.length];
 
     for (int i = 0; i < columns.length; i++) {
@@ -512,14 +512,8 @@
     return specs;
   }
 
-  public static SortSpec[] columnsToSortSpec(Collection<Column> columns) {
-    SortSpec[] specs = new SortSpec[columns.size()];
-    int i = 0;
-    for (Column column : columns) {
-      specs[i++] = new SortSpec(column, true, false);
-    }
-
-    return specs;
+  public static SortSpec[] columnsToSortSpecs(Collection<Column> columns) {
+    return columnsToSortSpecs(columns.toArray(new Column[columns.size()]));
   }
 
   public static Schema sortSpecsToSchema(SortSpec[] sortSpecs) {
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
index bae6e4a..8b34189 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/SimpleAlgebraVisitor.java
@@ -155,18 +155,13 @@
   }
 
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-  // Other Expressions
+  // Functions and General Set Function Section
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
   @Override
   public RESULT visitFunction(CONTEXT ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
     return super.visitFunction(ctx, stack, expr);
   }
 
-  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-  // General Set Section
-  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
-
   @Override
   public RESULT visitCountRowsFunction(CONTEXT ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
       throws PlanningException {
@@ -179,6 +174,11 @@
     return super.visitGeneralSetFunction(ctx, stack, expr);
   }
 
+  @Override
+  public RESULT visitWindowFunction(CONTEXT ctx, Stack<Expr> stack, WindowFunctionExpr expr) throws PlanningException {
+    return super.visitWindowFunction(ctx, stack, expr);
+  }
+
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
   // Literal Section
   ///////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -189,11 +189,6 @@
   }
 
   @Override
-  public RESULT visitCastExpr(CONTEXT ctx, Stack<Expr> stack, CastExpr expr) throws PlanningException {
-    return super.visitCastExpr(ctx, stack, expr);
-  }
-
-  @Override
   public RESULT visitLiteral(CONTEXT ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException {
     return super.visitLiteral(ctx, stack, expr);
   }
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/TypeDeterminant.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/TypeDeterminant.java
new file mode 100644
index 0000000..2e3c1c1
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/TypeDeterminant.java
@@ -0,0 +1,301 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner;
+
+import org.apache.tajo.algebra.*;
+import org.apache.tajo.catalog.CatalogService;
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.FunctionDesc;
+import org.apache.tajo.catalog.exception.NoSuchFunctionException;
+import org.apache.tajo.catalog.proto.CatalogProtos;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.engine.utils.DataTypeUtil;
+
+import java.util.Stack;
+
+import static org.apache.tajo.common.TajoDataTypes.DataType;
+import static org.apache.tajo.common.TajoDataTypes.Type.BOOLEAN;
+import static org.apache.tajo.common.TajoDataTypes.Type.NULL_TYPE;
+import static org.apache.tajo.engine.planner.LogicalPlanPreprocessor.PreprocessContext;
+
+public class TypeDeterminant extends SimpleAlgebraVisitor<PreprocessContext, DataType> {
+  private DataType BOOL_TYPE = CatalogUtil.newSimpleDataType(BOOLEAN);
+  private CatalogService catalog;
+
+  public TypeDeterminant(CatalogService catalog) {
+    this.catalog = catalog;
+  }
+
+  public DataType determineDataType(PreprocessContext ctx, Expr expr) throws PlanningException {
+    return visit(ctx, new Stack<Expr>(), expr);
+  }
+
+  @Override
+  public DataType visitUnaryOperator(PreprocessContext ctx, Stack<Expr> stack, UnaryOperator expr) throws PlanningException {
+    stack.push(expr);
+    DataType dataType = null;
+    switch (expr.getType()) {
+    case IsNullPredicate:
+    case ExistsPredicate:
+      dataType = BOOL_TYPE;
+      break;
+    case Cast:
+      dataType = LogicalPlanner.convertDataType(((CastExpr)expr).getTarget());
+      break;
+    default:
+      dataType = visit(ctx, stack, expr.getChild());
+    }
+
+    return dataType;
+  }
+
+  @Override
+  public DataType visitBinaryOperator(PreprocessContext ctx, Stack<Expr> stack, BinaryOperator expr)
+      throws PlanningException {
+    stack.push(expr);
+    DataType lhsType = visit(ctx, stack, expr.getLeft());
+    DataType rhsType = visit(ctx, stack, expr.getRight());
+    stack.pop();
+    return computeBinaryType(expr.getType(), lhsType, rhsType);
+  }
+
+  public DataType computeBinaryType(OpType type, DataType lhsDataType, DataType rhsDataType) throws PlanningException {
+    if(OpType.isLogicalType(type) || OpType.isComparisonType(type)) {
+      return BOOL_TYPE;
+    } else if (OpType.isArithmeticType(type)) {
+      return DataTypeUtil.determineType(lhsDataType, rhsDataType);
+    } else if (type == OpType.Concatenate) {
+      return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TEXT);
+    } else if (type == OpType.InPredicate) {
+      return BOOL_TYPE;
+    } else if (type == OpType.LikePredicate || type == OpType.SimilarToPredicate || type == OpType.Regexp) {
+      return BOOL_TYPE;
+    } else {
+      throw new PlanningException(type.name() + "is not binary type");
+    }
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Other Predicates Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public DataType visitBetween(PreprocessContext ctx, Stack<Expr> stack, BetweenPredicate expr)
+      throws PlanningException {
+    return CatalogUtil.newSimpleDataType(BOOLEAN);
+  }
+
+  @Override
+  public DataType visitCaseWhen(PreprocessContext ctx, Stack<Expr> stack, CaseWhenPredicate caseWhen)
+      throws PlanningException {
+    DataType lastDataType = null;
+
+    for (CaseWhenPredicate.WhenExpr when : caseWhen.getWhens()) {
+      DataType resultType = visit(ctx, stack, when.getResult());
+      if (lastDataType != null) {
+        lastDataType = ExprAnnotator.getWidestType(lastDataType, resultType);
+      } else {
+        lastDataType = resultType;
+      }
+    }
+
+    if (caseWhen.hasElseResult()) {
+      DataType elseResultType = visit(ctx, stack, caseWhen.getElseResult());
+      lastDataType = ExprAnnotator.getWidestType(lastDataType, elseResultType);
+    }
+
+    return lastDataType;
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Other Expressions
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public DataType visitColumnReference(PreprocessContext ctx, Stack<Expr> stack, ColumnReferenceExpr expr)
+      throws PlanningException {
+    stack.push(expr);
+    Column column = ctx.plan.resolveColumn(ctx.currentBlock, expr);
+    stack.pop();
+    return column.getDataType();
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // General Set Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public DataType visitFunction(PreprocessContext ctx, Stack<Expr> stack, FunctionExpr expr) throws PlanningException {
+    stack.push(expr); // <--- Push
+
+    // Given parameters
+    Expr[] params = expr.getParams();
+    if (params == null) {
+      params = new Expr[0];
+    }
+
+    DataType[] givenArgs = new DataType[params.length];
+    DataType[] paramTypes = new DataType[params.length];
+
+    for (int i = 0; i < params.length; i++) {
+      givenArgs[i] = visit(ctx, stack, params[i]);
+      paramTypes[i] = givenArgs[i];
+    }
+
+    stack.pop(); // <--- Pop
+
+    if (!catalog.containFunction(expr.getSignature(), paramTypes)) {
+      throw new NoSuchFunctionException(expr.getSignature(), paramTypes);
+    }
+
+    FunctionDesc funcDesc = catalog.getFunction(expr.getSignature(), paramTypes);
+    return funcDesc.getReturnType();
+  }
+
+  @Override
+  public DataType visitCountRowsFunction(PreprocessContext ctx, Stack<Expr> stack, CountRowsFunctionExpr expr)
+      throws PlanningException {
+    FunctionDesc countRows = catalog.getFunction("count", CatalogProtos.FunctionType.AGGREGATION,
+        new DataType[] {});
+    return countRows.getReturnType();
+  }
+
+  @Override
+  public DataType visitGeneralSetFunction(PreprocessContext ctx, Stack<Expr> stack, GeneralSetFunctionExpr setFunction)
+      throws PlanningException {
+    stack.push(setFunction);
+
+    Expr[] params = setFunction.getParams();
+    DataType[] givenArgs = new DataType[params.length];
+    DataType[] paramTypes = new DataType[params.length];
+
+    CatalogProtos.FunctionType functionType = setFunction.isDistinct() ?
+        CatalogProtos.FunctionType.DISTINCT_AGGREGATION : CatalogProtos.FunctionType.AGGREGATION;
+    givenArgs[0] = visit(ctx, stack, params[0]);
+    if (setFunction.getSignature().equalsIgnoreCase("count")) {
+      paramTypes[0] = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.ANY);
+    } else {
+      paramTypes[0] = givenArgs[0];
+    }
+
+    stack.pop(); // <-- pop
+
+    if (!catalog.containFunction(setFunction.getSignature(), functionType, paramTypes)) {
+      throw new NoSuchFunctionException(setFunction.getSignature(), paramTypes);
+    }
+
+    FunctionDesc funcDesc = catalog.getFunction(setFunction.getSignature(), functionType, paramTypes);
+    return funcDesc.getReturnType();
+  }
+
+  @Override
+  public DataType visitWindowFunction(PreprocessContext ctx, Stack<Expr> stack, WindowFunctionExpr windowFunc)
+      throws PlanningException {
+    stack.push(windowFunc); // <--- Push
+
+    String funcName = windowFunc.getSignature();
+    boolean distinct = windowFunc.isDistinct();
+    Expr[] params = windowFunc.getParams();
+    DataType[] givenArgs = new DataType[params.length];
+    TajoDataTypes.DataType[] paramTypes = new TajoDataTypes.DataType[params.length];
+    CatalogProtos.FunctionType functionType;
+
+    // Rewrite parameters if necessary
+    if (params.length > 0) {
+      givenArgs[0] = visit(ctx, stack, params[0]);
+
+      if (windowFunc.getSignature().equalsIgnoreCase("count")) {
+        paramTypes[0] = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.ANY);
+      } else if (windowFunc.getSignature().equalsIgnoreCase("row_number")) {
+        paramTypes[0] = CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT8);
+      } else {
+        paramTypes[0] = givenArgs[0];
+      }
+    }
+    stack.pop(); // <--- Pop
+
+    // TODO - containFunction and getFunction should support the function type mask which provides ORing multiple types.
+    // the below checking against WINDOW_FUNCTIONS is a workaround code for the above problem.
+    if (ExprAnnotator.WINDOW_FUNCTIONS.contains(funcName.toLowerCase())) {
+      if (distinct) {
+        throw new NoSuchFunctionException("row_number() does not support distinct keyword.");
+      }
+      functionType = CatalogProtos.FunctionType.WINDOW;
+    } else {
+      functionType = distinct ?
+          CatalogProtos.FunctionType.DISTINCT_AGGREGATION : CatalogProtos.FunctionType.AGGREGATION;
+    }
+
+    if (!catalog.containFunction(windowFunc.getSignature(), functionType, paramTypes)) {
+      throw new NoSuchFunctionException(funcName, paramTypes);
+    }
+
+    FunctionDesc funcDesc = catalog.getFunction(funcName, functionType, paramTypes);
+
+    return funcDesc.getReturnType();
+  }
+
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+  // Literal Section
+  ///////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+  @Override
+  public DataType visitDataType(PreprocessContext ctx, Stack<Expr> stack, DataTypeExpr expr) throws PlanningException {
+    return LogicalPlanner.convertDataType(expr);
+  }
+
+  @Override
+  public DataType visitLiteral(PreprocessContext ctx, Stack<Expr> stack, LiteralValue expr) throws PlanningException {
+    // It must be the same to ExprAnnotator::visitLiteral.
+
+    switch (expr.getValueType()) {
+    case Boolean:
+      return CatalogUtil.newSimpleDataType(BOOLEAN);
+    case String:
+      return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TEXT);
+    case Unsigned_Integer:
+      return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT4);
+    case Unsigned_Large_Integer:
+      return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT8);
+    case Unsigned_Float:
+      return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT8);
+    default:
+      throw new RuntimeException("Unsupported type: " + expr.getValueType());
+    }
+  }
+
+  @Override
+  public DataType visitNullLiteral(PreprocessContext ctx, Stack<Expr> stack, NullLiteral expr)
+      throws PlanningException {
+    return CatalogUtil.newSimpleDataType(NULL_TYPE);
+  }
+
+  @Override
+  public DataType visitTimestampLiteral(PreprocessContext ctx, Stack<Expr> stack, TimestampLiteral expr)
+      throws PlanningException {
+    return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TIMESTAMP);
+  }
+
+  @Override
+  public DataType visitTimeLiteral(PreprocessContext ctx, Stack<Expr> stack, TimeLiteral expr)
+      throws PlanningException {
+    return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TIME);
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
index edd5674..bb7f2e5 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/global/GlobalPlanner.java
@@ -756,7 +756,7 @@
 
     ExecutionBlock secondStage = context.plan.newExecutionBlock();
     secondStage.setPlan(secondPhaseGroupby);
-    SortSpec [] sortSpecs = PlannerUtil.columnsToSortSpec(firstStageGroupingColumns);
+    SortSpec [] sortSpecs = PlannerUtil.columnsToSortSpecs(firstStageGroupingColumns);
     secondStage.getEnforcer().enforceSortAggregation(secondPhaseGroupby.getPID(), sortSpecs);
 
     // Create a data channel between the first and second stages
@@ -1220,6 +1220,59 @@
     }
 
     @Override
+    public LogicalNode visitWindowAgg(GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
+                                    WindowAggNode node, Stack<LogicalNode> stack) throws PlanningException {
+      LogicalNode child = super.visitWindowAgg(context, plan, block, node, stack);
+
+      ExecutionBlock childBlock = context.execBlockMap.remove(child.getPID());
+      ExecutionBlock newExecBlock = buildWindowAgg(context, childBlock, node);
+      context.execBlockMap.put(newExecBlock.getPlan().getPID(), newExecBlock);
+
+      return newExecBlock.getPlan();
+    }
+
+    private ExecutionBlock buildWindowAgg(GlobalPlanContext context, ExecutionBlock lastBlock,
+                                        WindowAggNode windowAgg) throws PlanningException {
+      MasterPlan masterPlan = context.plan;
+
+      ExecutionBlock childBlock = lastBlock;
+      ExecutionBlock currentBlock = masterPlan.newExecutionBlock();
+      DataChannel channel;
+      if (windowAgg.hasPartitionKeys()) { // if there is at one distinct aggregation function
+        channel = new DataChannel(childBlock, currentBlock, RANGE_SHUFFLE, 32);
+        channel.setShuffleKeys(windowAgg.getPartitionKeys());
+      } else {
+        channel = new DataChannel(childBlock, currentBlock, HASH_SHUFFLE, 1);
+        channel.setShuffleKeys(null);
+      }
+      channel.setSchema(windowAgg.getInSchema());
+      channel.setStoreType(storeType);
+
+      LogicalNode childNode = windowAgg.getChild();
+      ScanNode scanNode = buildInputExecutor(masterPlan.getLogicalPlan(), channel);
+
+      if (windowAgg.hasPartitionKeys()) {
+        SortNode sortNode = masterPlan.getLogicalPlan().createNode(SortNode.class);
+        sortNode.setOutSchema(scanNode.getOutSchema());
+        sortNode.setInSchema(scanNode.getOutSchema());
+        sortNode.setSortSpecs(PlannerUtil.columnsToSortSpecs(windowAgg.getPartitionKeys()));
+        sortNode.setChild(childNode);
+        childBlock.setPlan(sortNode);
+
+        windowAgg.setChild(scanNode);
+      } else {
+        windowAgg.setInSchema(scanNode.getOutSchema());
+        windowAgg.setChild(scanNode);
+        childBlock.setPlan(childNode);
+      }
+
+      currentBlock.setPlan(windowAgg);
+      context.plan.addConnect(channel);
+
+      return currentBlock;
+    }
+
+    @Override
     public LogicalNode visitGroupBy(GlobalPlanContext context, LogicalPlan plan, LogicalPlan.QueryBlock block,
                                     GroupbyNode node, Stack<LogicalNode> stack) throws PlanningException {
       LogicalNode child = super.visitGroupBy(context, plan, block, node, stack);
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
index de79f93..fa1199b 100644
--- 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
@@ -35,6 +35,7 @@
   SORT(SortNode.class),
   HAVING(HavingNode.class),
   GROUP_BY(GroupbyNode.class),
+  WINDOW_AGG(WindowAggNode.class),
   SELECTION(SelectionNode.class),
   JOIN(JoinNode.class),
   UNION(UnionNode.class),
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/WindowAggNode.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/WindowAggNode.java
new file mode 100644
index 0000000..20b195f
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/WindowAggNode.java
@@ -0,0 +1,238 @@
+/**
+ * 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.Column;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.engine.eval.WindowFunctionEval;
+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 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;
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/WindowSpec.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/logical/WindowSpec.java
new file mode 100644
index 0000000..afec0ac
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/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.engine.planner.logical;
+
+
+import com.google.common.base.Objects;
+import com.google.gson.annotations.Expose;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.engine.eval.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);
+    }
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/physical/WindowAggExec.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/physical/WindowAggExec.java
new file mode 100644
index 0000000..7aeed13
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/physical/WindowAggExec.java
@@ -0,0 +1,340 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.planner.physical;
+
+import com.google.common.collect.Lists;
+import org.apache.tajo.catalog.Column;
+import org.apache.tajo.catalog.Schema;
+import org.apache.tajo.catalog.SortSpec;
+import org.apache.tajo.datum.Datum;
+import org.apache.tajo.engine.eval.WindowFunctionEval;
+import org.apache.tajo.engine.function.FunctionContext;
+import org.apache.tajo.engine.planner.logical.WindowAggNode;
+import org.apache.tajo.engine.planner.logical.WindowSpec;
+import org.apache.tajo.storage.Tuple;
+import org.apache.tajo.storage.TupleComparator;
+import org.apache.tajo.storage.VTuple;
+import org.apache.tajo.worker.TaskAttemptContext;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * The sort-based window aggregation operator
+ */
+public class WindowAggExec extends UnaryPhysicalExec {
+  // plan information
+  protected final int outputColumnNum;
+  protected final int nonFunctionColumnNum;
+  protected final int nonFunctionColumns[];
+
+  protected final int functionNum;
+  protected final WindowFunctionEval functions[];
+
+  protected Schema schemaForOrderBy;
+  protected int sortKeyColumns[];
+  protected final boolean hasPartitionKeys;
+  protected final int partitionKeyNum;
+  protected final int partitionKeyIds[];
+
+  // for evaluation
+  protected FunctionContext contexts [];
+  protected Tuple lastKey = null;
+  protected boolean noMoreTuples = false;
+  private boolean [] orderedFuncFlags;
+  private boolean [] aggFuncFlags;
+  private boolean [] windowFuncFlags;
+  private boolean [] endUnboundedFollowingFlags;
+  private boolean [] endCurrentRowFlags;
+
+  private boolean endCurrentRow = false;
+
+  // operator state
+  enum WindowState {
+    NEW_WINDOW,
+    ACCUMULATING_WINDOW,
+    EVALUATION,
+    RETRIEVING_FROM_WINDOW,
+    END_OF_TUPLE
+  }
+
+  // Transient state
+  boolean firstTime = true;
+  List<Tuple> evaluatedTuples = null;
+  List<Tuple> accumulatedInTuples = null;
+  List<Tuple> nextAccumulatedProjected = null;
+  List<Tuple> nextAccumulatedInTuples = null;
+  WindowState state = WindowState.NEW_WINDOW;
+  Iterator<Tuple> tupleInFrameIterator = null;
+
+  public WindowAggExec(TaskAttemptContext context, WindowAggNode plan, PhysicalExec child) throws IOException {
+    super(context, plan.getInSchema(), plan.getOutSchema(), child);
+
+    if (plan.hasPartitionKeys()) {
+      final Column[] keyColumns = plan.getPartitionKeys();
+      partitionKeyNum = keyColumns.length;
+      partitionKeyIds = new int[partitionKeyNum];
+      Column col;
+      for (int idx = 0; idx < plan.getPartitionKeys().length; idx++) {
+        col = keyColumns[idx];
+        partitionKeyIds[idx] = inSchema.getColumnId(col.getQualifiedName());
+      }
+      hasPartitionKeys = true;
+    } else {
+      partitionKeyNum = 0;
+      partitionKeyIds = null;
+      hasPartitionKeys = false;
+    }
+
+    if (plan.hasAggFunctions()) {
+      functions = plan.getWindowFunctions();
+      functionNum = functions.length;
+
+      orderedFuncFlags = new boolean[functions.length];
+      windowFuncFlags = new boolean[functions.length];
+      aggFuncFlags = new boolean[functions.length];
+
+      endUnboundedFollowingFlags = new boolean[functions.length];
+      endCurrentRowFlags = new boolean[functions.length];
+
+      List<Column> additionalSortKeyColumns = Lists.newArrayList();
+      Schema rewrittenSchema = new Schema(outSchema);
+      for (int i = 0; i < functions.length; i++) {
+        WindowSpec.WindowEndBound endBound = functions[i].getWindowFrame().getEndBound();
+        switch (endBound.getBoundType()) {
+        case CURRENT_ROW:
+          endCurrentRowFlags[i] = true; break;
+        case UNBOUNDED_FOLLOWING:
+          endUnboundedFollowingFlags[i] = true; break;
+        default:
+        }
+
+        switch (functions[i].getFuncDesc().getFuncType()) {
+        case AGGREGATION:
+        case DISTINCT_AGGREGATION:
+          aggFuncFlags[i] = true; break;
+        case WINDOW:
+          windowFuncFlags[i] = true; break;
+        default:
+        }
+
+        if (functions[i].hasSortSpecs()) {
+          orderedFuncFlags[i] = true;
+
+          for (SortSpec sortSpec : functions[i].getSortSpecs()) {
+            if (!rewrittenSchema.contains(sortSpec.getSortKey())) {
+              additionalSortKeyColumns.add(sortSpec.getSortKey());
+            }
+          }
+        }
+      }
+
+      sortKeyColumns = new int[additionalSortKeyColumns.size()];
+      schemaForOrderBy = new Schema(outSchema);
+      for (int i = 0; i < additionalSortKeyColumns.size(); i++) {
+        sortKeyColumns[i] = i;
+        schemaForOrderBy.addColumn(additionalSortKeyColumns.get(i));
+      }
+    } else {
+      functions = new WindowFunctionEval[0];
+      functionNum = 0;
+      schemaForOrderBy = outSchema;
+    }
+
+
+    nonFunctionColumnNum = plan.getTargets().length - functionNum;
+    nonFunctionColumns = new int[nonFunctionColumnNum];
+    for (int idx = 0; idx < plan.getTargets().length - functionNum; idx++) {
+      nonFunctionColumns[idx] = inSchema.getColumnId(plan.getTargets()[idx].getCanonicalName());
+    }
+
+    outputColumnNum = nonFunctionColumnNum + functionNum;
+  }
+
+  private void transition(WindowState state) {
+    this.state = state;
+  }
+
+  @Override
+  public Tuple next() throws IOException {
+    Tuple currentKey = null;
+    Tuple readTuple = null;
+
+    while(!context.isStopped() && state != WindowState.END_OF_TUPLE) {
+
+      if (state == WindowState.NEW_WINDOW) {
+        initWindow();
+        transition(WindowState.ACCUMULATING_WINDOW);
+      }
+
+      if (state != WindowState.RETRIEVING_FROM_WINDOW) { // read an input tuple and build a partition key
+        readTuple = child.next();
+
+        if (readTuple == null) { // the end of tuple
+          noMoreTuples = true;
+          transition(WindowState.EVALUATION);
+        }
+
+        if (readTuple != null && hasPartitionKeys) { // get a key tuple
+          currentKey = new VTuple(partitionKeyIds.length);
+          for (int i = 0; i < partitionKeyIds.length; i++) {
+            currentKey.put(i, readTuple.get(partitionKeyIds[i]));
+          }
+        }
+      }
+
+      if (state == WindowState.ACCUMULATING_WINDOW) {
+        accumulatingWindow(currentKey, readTuple);
+      }
+
+      if (state == WindowState.EVALUATION) {
+        evaluationWindowFrame();
+
+        tupleInFrameIterator = evaluatedTuples.iterator();
+        transition(WindowState.RETRIEVING_FROM_WINDOW);
+      }
+
+      if (state == WindowState.RETRIEVING_FROM_WINDOW) {
+        if (tupleInFrameIterator.hasNext()) {
+          return tupleInFrameIterator.next();
+        } else {
+          finalizeWindow();
+        }
+      }
+    }
+
+    return null;
+  }
+
+  private void initWindow() {
+    if (firstTime) {
+      accumulatedInTuples = Lists.newArrayList();
+
+      contexts = new FunctionContext[functionNum];
+      for(int evalIdx = 0; evalIdx < functionNum; evalIdx++) {
+        contexts[evalIdx] = functions[evalIdx].newContext();
+      }
+      firstTime = false;
+    }
+  }
+
+  private void accumulatingWindow(Tuple currentKey, Tuple inTuple) {
+
+    if (lastKey == null || lastKey.equals(currentKey)) { // if the current key is same to the previous key
+      accumulatedInTuples.add(new VTuple(inTuple));
+
+    } else {
+      // if the current key is different from the previous key,
+      // the current key belongs to the next window frame. preaccumulatingNextWindow() will
+      // aggregate the current key for next window frame.
+      preAccumulatingNextWindow(inTuple);
+      transition(WindowState.EVALUATION);
+    }
+
+    lastKey = currentKey;
+  }
+
+  private void preAccumulatingNextWindow(Tuple inTuple) {
+    Tuple projectedTuple = new VTuple(outSchema.size());
+    for(int idx = 0; idx < nonFunctionColumnNum; idx++) {
+      projectedTuple.put(idx, inTuple.get(nonFunctionColumns[idx]));
+    }
+    nextAccumulatedProjected = Lists.newArrayList();
+    nextAccumulatedProjected.add(projectedTuple);
+    nextAccumulatedInTuples = Lists.newArrayList();
+    nextAccumulatedInTuples.add(new VTuple(inTuple));
+  }
+
+  private void evaluationWindowFrame() {
+    TupleComparator comp;
+
+    evaluatedTuples = new ArrayList<Tuple>();
+
+    for (int i = 0; i <accumulatedInTuples.size(); i++) {
+      Tuple inTuple = accumulatedInTuples.get(i);
+
+      Tuple projectedTuple = new VTuple(schemaForOrderBy.size());
+      for (int c = 0; c < nonFunctionColumnNum; c++) {
+        projectedTuple.put(c, inTuple.get(nonFunctionColumns[c]));
+      }
+      for (int c = 0; c < sortKeyColumns.length; c++) {
+        projectedTuple.put(outputColumnNum + c, inTuple.get(sortKeyColumns[c]));
+      }
+
+      evaluatedTuples.add(projectedTuple);
+    }
+
+    for (int idx = 0; idx < functions.length; idx++) {
+      if (orderedFuncFlags[idx]) {
+        comp = new TupleComparator(inSchema, functions[idx].getSortSpecs());
+        Collections.sort(accumulatedInTuples, comp);
+        comp = new TupleComparator(schemaForOrderBy, functions[idx].getSortSpecs());
+        Collections.sort(evaluatedTuples, comp);
+      }
+
+      for (int i = 0; i < accumulatedInTuples.size(); i++) {
+        Tuple inTuple = accumulatedInTuples.get(i);
+        Tuple outTuple = evaluatedTuples.get(i);
+
+        functions[idx].merge(contexts[idx], inSchema, inTuple);
+
+        if (windowFuncFlags[idx]) {
+          Datum result = functions[idx].terminate(contexts[idx]);
+          outTuple.put(nonFunctionColumnNum + idx, result);
+        }
+      }
+
+      if (aggFuncFlags[idx]) {
+        Datum result = functions[idx].terminate(contexts[idx]);
+        for (int i = 0; i < evaluatedTuples.size(); i++) {
+          Tuple outTuple = evaluatedTuples.get(i);
+          outTuple.put(nonFunctionColumnNum + idx, result);
+        }
+      }
+    }
+  }
+
+  private void finalizeWindow() {
+    evaluatedTuples.clear();
+    accumulatedInTuples.clear();
+
+    if (noMoreTuples) {
+      transition(WindowState.END_OF_TUPLE);
+    } else {
+      accumulatedInTuples = nextAccumulatedInTuples;
+
+      contexts = new FunctionContext[functionNum];
+      for(int evalIdx = 0; evalIdx < functionNum; evalIdx++) {
+        contexts[evalIdx] = functions[evalIdx].newContext();
+      }
+      transition(WindowState.NEW_WINDOW);
+    }
+  }
+
+  @Override
+  public void rescan() throws IOException {
+    super.rescan();
+
+    lastKey = null;
+    noMoreTuples = false;
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
index 4215423..34c88a6 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/FilterPushDownRule.java
@@ -18,8 +18,7 @@
 
 package org.apache.tajo.engine.planner.rewrite;
 
-import com.google.common.collect.Lists;
-import com.google.common.collect.Sets;
+import com.google.common.collect.*;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.tajo.algebra.JoinType;
@@ -464,7 +463,6 @@
 
     context.setFiltersTobePushed(new HashSet<EvalNode>(transformedMap.keySet()));
     visit(context, plan, plan.getBlock(node.getSubQuery()));
-
     context.setToOrigin(transformedMap);
 
     return node;
@@ -512,18 +510,18 @@
     List<EvalNode> notMatched = new ArrayList<EvalNode>();
 
     //copy -> origin
-    Map<EvalNode, EvalNode> matched = findCanPushdownAndTransform(
+    BiMap<EvalNode, EvalNode> transformedMap = findCanPushdownAndTransform(
         context, projectionNode, childNode, notMatched, null, false, 0);
 
-    context.setFiltersTobePushed(matched.keySet());
+    context.setFiltersTobePushed(transformedMap.keySet());
 
     stack.push(projectionNode);
-    LogicalNode current = visit(context, plan, plan.getBlock(childNode), childNode, stack);
+    childNode = visit(context, plan, plan.getBlock(childNode), childNode, stack);
     stack.pop();
 
     // find not matched after visiting child
     for (EvalNode eval: context.pushingDownFilters) {
-      notMatched.add(matched.get(eval));
+      notMatched.add(transformedMap.get(eval));
     }
 
     EvalNode qual = null;
@@ -536,24 +534,39 @@
     }
 
     // If there is not matched node add SelectionNode and clear context.pushingDownFilters
-    if (qual != null) {
+    if (qual != null && LogicalPlanner.checkIfBeEvaluatedAtThis(qual, projectionNode)) {
       SelectionNode selectionNode = plan.createNode(SelectionNode.class);
-      selectionNode.setInSchema(current.getOutSchema());
-      selectionNode.setOutSchema(current.getOutSchema());
+      selectionNode.setInSchema(childNode.getOutSchema());
+      selectionNode.setOutSchema(childNode.getOutSchema());
       selectionNode.setQual(qual);
       block.registerNode(selectionNode);
 
       projectionNode.setChild(selectionNode);
-      selectionNode.setChild(current);
+      selectionNode.setChild(childNode);
+
+      // clean all remain filters because all conditions are merged into a qual
+      context.pushingDownFilters.clear();
     }
 
-    //notify all eval matched to upper
-    context.pushingDownFilters.clear();
+    // if there are remain filters, recover the original names and give back to the upper query block.
+    if (context.pushingDownFilters.size() > 0) {
+      ImmutableSet<EvalNode> copy = ImmutableSet.copyOf(context.pushingDownFilters);
+      context.pushingDownFilters.clear();
+      context.pushingDownFilters.addAll(reverseTransform(transformedMap, copy));
+    }
 
-    return current;
+    return projectionNode;
   }
 
-  private Map<EvalNode, EvalNode> findCanPushdownAndTransform(
+  private Collection<EvalNode> reverseTransform(BiMap<EvalNode, EvalNode> map, Set<EvalNode> remainFilters) {
+    Set<EvalNode> reversed = Sets.newHashSet();
+    for (EvalNode evalNode : remainFilters) {
+      reversed.add(map.get(evalNode));
+    }
+    return reversed;
+  }
+
+  private BiMap<EvalNode, EvalNode> findCanPushdownAndTransform(
       FilterPushDownContext context, Projectable node,
       LogicalNode childNode, List<EvalNode> notMatched,
       Set<String> partitionColumns,
@@ -565,7 +578,7 @@
     }
 
     // copy -> origin
-    Map<EvalNode, EvalNode> matched = new HashMap<EvalNode, EvalNode>();
+    BiMap<EvalNode, EvalNode> matched = HashBiMap.create();
 
     for (EvalNode eval : context.pushingDownFilters) {
       if (ignoreJoin && EvalTreeUtil.isJoinQual(eval, true)) {
@@ -754,6 +767,17 @@
   }
 
   @Override
+  public LogicalNode visitWindowAgg(FilterPushDownContext context, LogicalPlan plan,
+                                  LogicalPlan.QueryBlock block, WindowAggNode winAggNode,
+                                  Stack<LogicalNode> stack) throws PlanningException {
+    stack.push(winAggNode);
+    super.visitWindowAgg(context, plan, block, winAggNode, stack);
+    stack.pop();
+    return winAggNode;
+  }
+
+
+  @Override
   public LogicalNode visitGroupBy(FilterPushDownContext context, LogicalPlan plan,
                                   LogicalPlan.QueryBlock block, GroupbyNode groupbyNode,
                                   Stack<LogicalNode> stack) throws PlanningException {
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/ProjectionPushDownRule.java b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/ProjectionPushDownRule.java
index 4e4b5c3..2bc210c 100644
--- a/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/ProjectionPushDownRule.java
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/planner/rewrite/ProjectionPushDownRule.java
@@ -592,6 +592,99 @@
     return node;
   }
 
+  public LogicalNode visitWindowAgg(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, WindowAggNode node,
+                        Stack<LogicalNode> stack) throws PlanningException {
+    Context newContext = new Context(context);
+
+    if (node.hasPartitionKeys()) {
+      for (Column c : node.getPartitionKeys()) {
+        newContext.addNecessaryReferences(new FieldEval(c));
+      }
+    }
+
+    if (node.hasSortSpecs()) {
+      for (SortSpec sortSpec : node.getSortSpecs()) {
+        newContext.addNecessaryReferences(new FieldEval(sortSpec.getSortKey()));
+      }
+    }
+
+    for (WindowFunctionEval winFunc : node.getWindowFunctions()) {
+      if (winFunc.hasSortSpecs()) {
+        for (SortSpec sortSpec : winFunc.getSortSpecs()) {
+          newContext.addNecessaryReferences(new FieldEval(sortSpec.getSortKey()));
+        }
+      }
+    }
+
+
+    int nonFunctionColumnNum = node.getTargets().length - node.getWindowFunctions().length;
+    LinkedHashSet<String> nonFunctionColumns = Sets.newLinkedHashSet();
+    for (int i = 0; i < nonFunctionColumnNum; i++) {
+      FieldEval fieldEval = (new FieldEval(node.getTargets()[i].getNamedColumn()));
+      nonFunctionColumns.add(newContext.addExpr(fieldEval));
+    }
+
+    final String [] aggEvalNames;
+    if (node.hasAggFunctions()) {
+      final int evalNum = node.getWindowFunctions().length;
+      aggEvalNames = new String[evalNum];
+      for (int evalIdx = 0, targetIdx = nonFunctionColumnNum; targetIdx < node.getTargets().length; evalIdx++,
+          targetIdx++) {
+        Target target = node.getTargets()[targetIdx];
+        WindowFunctionEval winFunc = node.getWindowFunctions()[evalIdx];
+        aggEvalNames[evalIdx] = newContext.addExpr(new Target(winFunc, target.getCanonicalName()));
+      }
+    } else {
+      aggEvalNames = null;
+    }
+
+    // visit a child node
+    LogicalNode child = super.visitWindowAgg(newContext, plan, block, node, stack);
+
+    node.setInSchema(child.getOutSchema());
+
+    List<Target> targets = Lists.newArrayList();
+    if (nonFunctionColumnNum > 0) {
+      for (String column : nonFunctionColumns) {
+        Target target = context.targetListMgr.getTarget(column);
+
+        // it rewrite grouping keys.
+        // This rewrite sets right column names and eliminates duplicated grouping keys.
+        if (context.targetListMgr.isEvaluated(column)) {
+          targets.add(new Target(new FieldEval(target.getNamedColumn())));
+        } else {
+          if (target.getEvalTree().getType() == EvalType.FIELD) {
+           targets.add(target);
+          }
+        }
+      }
+    }
+
+    // Getting projected targets
+    if (node.hasAggFunctions() && aggEvalNames != null) {
+      WindowFunctionEval [] aggEvals = new WindowFunctionEval[aggEvalNames.length];
+      int i = 0;
+      for (Iterator<String> it = getFilteredReferences(aggEvalNames, TUtil.newList(aggEvalNames)); it.hasNext();) {
+
+        String referenceName = it.next();
+        Target target = context.targetListMgr.getTarget(referenceName);
+
+        if (LogicalPlanner.checkIfBeEvaluatedAtWindowAgg(target.getEvalTree(), node)) {
+          aggEvals[i++] = target.getEvalTree();
+          context.targetListMgr.markAsEvaluated(target);
+
+          targets.add(new Target(new FieldEval(target.getNamedColumn())));
+        }
+      }
+      if (aggEvals.length > 0) {
+        node.setWindowFunctions(aggEvals);
+      }
+    }
+
+    node.setTargets(targets.toArray(new Target[targets.size()]));
+    return node;
+  }
+
   public LogicalNode visitGroupBy(Context context, LogicalPlan plan, LogicalPlan.QueryBlock block, GroupbyNode node,
                              Stack<LogicalNode> stack) throws PlanningException {
     Context newContext = new Context(context);
diff --git a/tajo-core/src/main/java/org/apache/tajo/engine/utils/DataTypeUtil.java b/tajo-core/src/main/java/org/apache/tajo/engine/utils/DataTypeUtil.java
new file mode 100644
index 0000000..084db21
--- /dev/null
+++ b/tajo-core/src/main/java/org/apache/tajo/engine/utils/DataTypeUtil.java
@@ -0,0 +1,121 @@
+/**
+ * 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.utils;
+
+import org.apache.tajo.catalog.CatalogUtil;
+import org.apache.tajo.common.TajoDataTypes;
+import org.apache.tajo.engine.eval.InvalidEvalException;
+
+public class DataTypeUtil {
+  /**
+   * This is verified by ExprsVerifier.checkArithmeticOperand().
+   */
+  public static TajoDataTypes.DataType determineType(TajoDataTypes.DataType left, TajoDataTypes.DataType right) throws InvalidEvalException {
+    switch (left.getType()) {
+
+    case INT1:
+    case INT2:
+    case INT4: {
+      switch(right.getType()) {
+      case INT1:
+      case INT2:
+      case INT4: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT4);
+      case INT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT8);
+      case FLOAT4: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT4);
+      case FLOAT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT8);
+      case DATE: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.DATE);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    case INT8: {
+      switch(right.getType()) {
+      case INT1:
+      case INT2:
+      case INT4:
+      case INT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT8);
+      case FLOAT4: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT4);
+      case FLOAT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT8);
+      case DATE: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.DATE);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    case FLOAT4: {
+      switch(right.getType()) {
+      case INT1:
+      case INT2:
+      case INT4: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT4);
+      case INT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT4);
+      case FLOAT4: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT4);
+      case FLOAT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT8);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    case FLOAT8: {
+      switch(right.getType()) {
+      case INT1:
+      case INT2:
+      case INT4:
+      case INT8:
+      case FLOAT4:
+      case FLOAT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.FLOAT8);
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    case DATE: {
+      switch(right.getType()) {
+      case INT2:
+      case INT4:
+      case INT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.DATE);
+      case INTERVAL:
+      case TIME: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TIMESTAMP);
+      case DATE: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT4);
+      }
+    }
+
+    case TIME: {
+      switch(right.getType()) {
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TIME);
+      case TIME: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      case DATE: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INT4);
+      }
+    }
+
+    case TIMESTAMP: {
+      switch (right.getType()) {
+      case INTERVAL: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.TIMESTAMP);
+      case TIMESTAMP: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    case INTERVAL: {
+      switch (right.getType()) {
+      case INTERVAL:
+      case FLOAT4:
+      case FLOAT8: return CatalogUtil.newSimpleDataType(TajoDataTypes.Type.INTERVAL);
+      }
+    }
+
+    default: return left;
+    }
+  }
+}
diff --git a/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java b/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
index 8954df1..94c4f25 100644
--- a/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
+++ b/tajo-core/src/main/java/org/apache/tajo/master/GlobalEngine.java
@@ -471,6 +471,7 @@
       LOG.debug("Non Optimized Query: \n" + plan.toString());
       LOG.debug("=============================================");
     }
+    LOG.info("Non Optimized Query: \n" + plan.toString());
     optimizer.optimize(session, plan);
     LOG.info("=============================================");
     LOG.info("Optimized Query: \n" + plan.toString());
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java b/tajo-core/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
index 1dc54f6..23314dd 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/parser/TestSQLAnalyzer.java
@@ -57,7 +57,7 @@
     String result = FileUtil.readTextFileFromResource("results/TestSQLAnalyzer/" + resultFileName);
 
     Expr expr = parseQuery(sql);
-    assertEquals(expr.toJson().trim(), result.trim());
+    assertEquals(result.trim(), expr.toJson().trim());
   }
 
 
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestGroupByQuery.java b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestGroupByQuery.java
index b5fd9f1..9f06317 100644
--- a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestGroupByQuery.java
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestGroupByQuery.java
@@ -280,7 +280,7 @@
         "from table10 group by id");
     String result = resultSetToString(res);
 
-    String expected = "id,?count,?avg_1,?min_2,?max_3,?sum_4,?cast_5,?cast_6,?cast_7,?cast_8\n" +
+    String expected = "id,?count_4,?avg_5,?min_6,?max_7,?sum_8,?cast_9,?cast_10,?cast_11,?cast_12\n" +
         "-------------------------------\n" +
         "1,2,4.0,0,5,12,4,0,5,12\n" +
         "2,3,2.0,0,3,6,7,0,8,21\n";
diff --git a/tajo-core/src/test/java/org/apache/tajo/engine/query/TestWindowQuery.java b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestWindowQuery.java
new file mode 100644
index 0000000..d202153
--- /dev/null
+++ b/tajo-core/src/test/java/org/apache/tajo/engine/query/TestWindowQuery.java
@@ -0,0 +1,240 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.tajo.engine.query;
+
+import org.apache.tajo.IntegrationTest;
+import org.apache.tajo.QueryTestCaseBase;
+import org.apache.tajo.TajoConstants;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.sql.ResultSet;
+
+@Category(IntegrationTest.class)
+public class TestWindowQuery extends QueryTestCaseBase {
+
+  public TestWindowQuery() {
+    super(TajoConstants.DEFAULT_DATABASE_NAME);
+  }
+
+  @Test
+  public final void testWindow1() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow2() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow3() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow4() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow5() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow6() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindow7() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithOrderBy1() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithOrderBy2() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithOrderBy3() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithOrderBy4() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithOrderBy5() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowBeforeLimit() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery2() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery3() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery4() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery5() throws Exception {
+    // filter push down test
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithSubQuery6() throws Exception {
+    // filter push down test
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation1() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation2() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation3() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation4() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation5() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testWindowWithAggregation6() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void testComplexOrderBy1() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void rowNumber1() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void rowNumber2() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+
+  @Test
+  public final void rowNumber3() throws Exception {
+    ResultSet res = executeQuery();
+    assertResultSet(res);
+    cleanupQuery(res);
+  }
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestLogicalPlanner/window1.sql b/tajo-core/src/test/resources/queries/TestLogicalPlanner/window1.sql
new file mode 100644
index 0000000..9d30145
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestLogicalPlanner/window1.sql
@@ -0,0 +1,5 @@
+ SELECT
+  l_orderkey,
+  sum(l_partkey) over()
+FROM
+  lineitem;
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window1.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window1.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window1.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window1.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window2.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window2.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window2.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window2.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window3.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window3.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window3.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window3.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window4.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window4.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window4.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window4.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window5.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window5.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window5.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window5.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window6.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window6.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window6.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window6.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window7.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window7.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window7.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window7.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window8.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window8.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window8.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window8.sql
diff --git a/tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window9.sql b/tajo-core/src/test/resources/queries/TestSQLAnalyzer/window9.sql
similarity index 100%
rename from tajo-core/tajo-core-backend/src/test/resources/queries/TestSQLAnalyzer/window9.sql
rename to tajo-core/src/test/resources/queries/TestSQLAnalyzer/window9.sql
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber1.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber1.sql
new file mode 100644
index 0000000..14a5128
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber1.sql
@@ -0,0 +1,5 @@
+SELECT
+  l_orderkey,
+  row_number() OVER () as row_num
+FROM
+  LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber2.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber2.sql
new file mode 100644
index 0000000..2e45120
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber2.sql
@@ -0,0 +1,5 @@
+SELECT
+  l_orderkey,
+  row_number() OVER (PARTITION BY L_ORDERKEY) as row_num
+FROM
+  LINEITEM
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber3.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber3.sql
new file mode 100644
index 0000000..44594c7
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/rowNumber3.sql
@@ -0,0 +1,7 @@
+SELECT
+  l_orderkey,
+  row_number() OVER (PARTITION BY L_ORDERKEY) as row_num,
+  l_discount,
+  avg(l_discount) OVER (PARTITION BY L_ORDERKEY) as average
+FROM
+  LINEITEM
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testComplexOrderBy1.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testComplexOrderBy1.sql
new file mode 100644
index 0000000..171815c
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testComplexOrderBy1.sql
@@ -0,0 +1,5 @@
+select
+  l_orderkey,
+  row_number() over (order by l_quantity * (1 - l_discount)) row_num
+from
+  lineitem
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow1.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow1.sql
new file mode 100644
index 0000000..8e6d083
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow1.sql
@@ -0,0 +1 @@
+SELECT sum(l_quantity) OVER () FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow2.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow2.sql
new file mode 100644
index 0000000..baa92f6
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow2.sql
@@ -0,0 +1 @@
+SELECT l_orderkey, l_quantity, sum(l_quantity) OVER () FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow3.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow3.sql
new file mode 100644
index 0000000..e4b92f0
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow3.sql
@@ -0,0 +1 @@
+SELECT l_orderkey, l_quantity, sum(l_quantity) OVER (PARTITION BY l_orderkey) FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow4.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow4.sql
new file mode 100644
index 0000000..d32c63a
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow4.sql
@@ -0,0 +1 @@
+SELECT l_orderkey, l_discount, sum(l_discount) OVER (PARTITION BY l_orderkey), sum(l_quantity) OVER (PARTITION BY l_orderkey) FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow5.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow5.sql
new file mode 100644
index 0000000..7f31991
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow5.sql
@@ -0,0 +1 @@
+SELECT l_orderkey, sum(l_discount) OVER (PARTITION BY l_orderkey), l_discount, sum(l_quantity) OVER (PARTITION BY l_orderkey) FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow6.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow6.sql
new file mode 100644
index 0000000..c1e4a84
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow6.sql
@@ -0,0 +1 @@
+SELECT l_orderkey, l_discount, row_number() OVER (PARTITION BY l_orderkey) r1 , sum(l_discount) OVER (PARTITION BY l_orderkey) r2 FROM LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow7.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow7.sql
new file mode 100644
index 0000000..2caf0db
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindow7.sql
@@ -0,0 +1 @@
+select l_orderkey, l_quantity, rank() over (partition by l_orderkey) as r from lineitem
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowBeforeLimit.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowBeforeLimit.sql
new file mode 100644
index 0000000..c4d7fa6
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowBeforeLimit.sql
@@ -0,0 +1,6 @@
+select
+  r_name,
+  rank() over (order by r_regionkey) as ran
+from
+  region
+limit 3;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation1.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation1.sql
new file mode 100644
index 0000000..7afdae2
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation1.sql
@@ -0,0 +1,4 @@
+select
+  row_number() over (order by count(*) desc) row_num
+from
+  lineitem
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation2.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation2.sql
new file mode 100644
index 0000000..1d77ca1
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation2.sql
@@ -0,0 +1,7 @@
+select
+  l_orderkey,
+  row_number() over (partition by l_orderkey order by count(*) desc) row_num
+from
+  lineitem
+group by
+  l_orderkey;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation3.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation3.sql
new file mode 100644
index 0000000..f3d3703
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation3.sql
@@ -0,0 +1,5 @@
+select
+  count(*) as cnt,
+  row_number() over (order by count(*) desc) row_num
+from
+  lineitem
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation4.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation4.sql
new file mode 100644
index 0000000..af695ba
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation4.sql
@@ -0,0 +1,8 @@
+select
+  l_orderkey,
+  count(*) as cnt,
+  row_number() over (order by count(*) desc) row_num
+from
+  lineitem
+group by
+  l_orderkey
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation5.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation5.sql
new file mode 100644
index 0000000..2764ae9
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation5.sql
@@ -0,0 +1,9 @@
+select
+  l_orderkey,
+  count(*) as cnt,
+  row_number() over (partition by l_orderkey order by count(*) desc)
+  row_num
+from
+  lineitem
+group by
+  l_orderkey
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation6.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation6.sql
new file mode 100644
index 0000000..1a9d46b
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithAggregation6.sql
@@ -0,0 +1,10 @@
+select
+  l_orderkey,
+  count(*) as cnt,
+  row_number() over (order by count(*) desc) row_num
+from
+  lineitem
+group by
+  l_orderkey
+order by
+  l_orderkey;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy1.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy1.sql
new file mode 100644
index 0000000..d047bbd
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy1.sql
@@ -0,0 +1,6 @@
+SELECT
+  l_orderkey,
+  l_discount,
+  rank() OVER (ORDER BY l_discount) r1
+FROM
+  LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy2.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy2.sql
new file mode 100644
index 0000000..7e6b339
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy2.sql
@@ -0,0 +1,6 @@
+SELECT
+  l_orderkey,
+  l_partkey,
+  rank() OVER (PARTITION BY L_ORDERKEY ORDER BY l_partkey) r1
+FROM
+  LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy3.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy3.sql
new file mode 100644
index 0000000..5fc7317
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy3.sql
@@ -0,0 +1,6 @@
+SELECT
+  l_orderkey,
+  l_partkey,
+  rank() OVER (PARTITION BY L_ORDERKEY ORDER BY l_partkey desc) r1
+FROM
+  LINEITEM
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy4.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy4.sql
new file mode 100644
index 0000000..26cfaa7
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy4.sql
@@ -0,0 +1,7 @@
+SELECT
+  l_orderkey,
+  l_partkey,
+  rank() OVER (ORDER BY l_orderkey) r1,
+  rank() OVER(ORDER BY l_partkey desc) r2
+FROM
+  LINEITEM;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy5.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy5.sql
new file mode 100644
index 0000000..2dc87af
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithOrderBy5.sql
@@ -0,0 +1,9 @@
+SELECT
+  l_orderkey,
+  l_partkey,
+  rank() OVER (ORDER BY l_orderkey) r1,
+  rank() OVER(ORDER BY l_partkey desc) r2
+FROM
+  LINEITEM
+where
+  l_partkey > 0 and l_partkey < 100;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery.sql
new file mode 100644
index 0000000..5fa6d00
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery.sql
@@ -0,0 +1,14 @@
+select
+  r_name,
+  c,
+  rank() over (order by r_regionkey) as ran
+from (
+  select
+    r_name,
+    r_regionkey,
+    count(*) as c
+  from
+    region
+  group by
+    r_name, r_regionkey
+) a;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery2.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery2.sql
new file mode 100644
index 0000000..87520fb
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery2.sql
@@ -0,0 +1,15 @@
+select
+  r_name,
+  c,
+  rank() over (partition by r_regionkey order by r_regionkey) as ran
+from (
+  select
+    r_name,
+    r_regionkey,
+    count(*) as c
+  from
+    region
+  group by
+    r_name, r_regionkey
+) a
+limit 3;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery3.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery3.sql
new file mode 100644
index 0000000..10d8863
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery3.sql
@@ -0,0 +1,14 @@
+select
+  a.r_name,
+  a.r_regionkey,
+  row_number() over (partition by a.r_name order by a.cnt desc) mk
+from (
+  select
+    r_name,
+    r_regionkey,
+    count(*) cnt
+  from
+    default.region
+  group by
+    r_name, r_regionkey
+) a;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery4.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery4.sql
new file mode 100644
index 0000000..fe32eb1
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery4.sql
@@ -0,0 +1,14 @@
+select
+  a.r_name,
+  a.r_regionkey,
+  row_number() over (partition by a.r_regionkey order by a.cnt desc) mk
+from (
+  select
+    r_name,
+    r_regionkey,
+    count(*) cnt
+  from
+    default.region
+  group by
+    r_name, r_regionkey
+) a;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery5.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery5.sql
new file mode 100644
index 0000000..2db397b
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery5.sql
@@ -0,0 +1,11 @@
+select
+  *
+from (
+  select
+    r_name,
+    rank() over (order by r_regionkey) as ran
+  from
+    region
+) a
+where
+  ran >= 3;
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery6.sql b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery6.sql
new file mode 100644
index 0000000..8678fe6
--- /dev/null
+++ b/tajo-core/src/test/resources/queries/TestWindowQuery/testWindowWithSubQuery6.sql
@@ -0,0 +1,9 @@
+select
+  *
+from (
+  select
+    r_name,
+    rank() over (order by r_regionkey) as ran from region
+) a
+where
+  r_name LIKE 'ASIA'
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window1.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window1.result
new file mode 100644
index 0000000..a21e755
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window1.result
@@ -0,0 +1,37 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {},
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window2.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window2.result
new file mode 100644
index 0000000..3708802
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window2.result
@@ -0,0 +1,44 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "ColumnName": "dt",
+              "OpType": "Column"
+            }
+          ]
+        },
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window3.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window3.result
new file mode 100644
index 0000000..fca2651
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window3.result
@@ -0,0 +1,50 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "Signature": "round",
+              "FuncParams": [
+                {
+                  "ColumnName": "dt",
+                  "OpType": "Column"
+                }
+              ],
+              "OpType": "Function"
+            }
+          ]
+        },
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window4.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window4.result
new file mode 100644
index 0000000..45ccdcc
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window4.result
@@ -0,0 +1,48 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "sortSpecs": [
+            {
+              "SortKey": {
+                "ColumnName": "dt",
+                "OpType": "Column"
+              },
+              "IsAsc": true,
+              "IsNullFirst": false
+            }
+          ]
+        },
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window5.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window5.result
new file mode 100644
index 0000000..aa00a09
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window5.result
@@ -0,0 +1,77 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "ColumnName": "dt2",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "Signature": "round",
+              "FuncParams": [
+                {
+                  "ColumnName": "dt",
+                  "OpType": "Column"
+                }
+              ],
+              "OpType": "Function"
+            },
+            {
+              "ColumnName": "dt2",
+              "OpType": "Column"
+            }
+          ],
+          "sortSpecs": [
+            {
+              "SortKey": {
+                "Signature": "ceil",
+                "FuncParams": [
+                  {
+                    "ColumnName": "dt",
+                    "OpType": "Column"
+                  }
+                ],
+                "OpType": "Function"
+              },
+              "IsAsc": true,
+              "IsNullFirst": false
+            }
+          ]
+        },
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window6.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window6.result
new file mode 100644
index 0000000..2c990c6
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window6.result
@@ -0,0 +1,83 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "ColumnName": "dt2",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "Signature": "round",
+              "FuncParams": [
+                {
+                  "ColumnName": "dt",
+                  "OpType": "Column"
+                }
+              ],
+              "OpType": "Function"
+            },
+            {
+              "ColumnName": "dt2",
+              "OpType": "Column"
+            }
+          ],
+          "sortSpecs": [
+            {
+              "SortKey": {
+                "Signature": "ceil",
+                "FuncParams": [
+                  {
+                    "ColumnName": "dt",
+                    "OpType": "Column"
+                  }
+                ],
+                "OpType": "Function"
+              },
+              "IsAsc": true,
+              "IsNullFirst": false
+            }
+          ],
+          "windowFrame": {
+            "unit": "ROW",
+            "startBound": {
+              "boundType": "UNBOUNDED_PRECEDING"
+            }
+          }
+        },
+        "IsDistinct": false,
+        "Signature": "sum",
+        "FuncParams": [
+          {
+            "ColumnName": "xy",
+            "OpType": "Column"
+          }
+        ],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window7.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window7.result
new file mode 100644
index 0000000..efb3878
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window7.result
@@ -0,0 +1,81 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "ColumnName": "dt2",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "Signature": "round",
+              "FuncParams": [
+                {
+                  "ColumnName": "dt",
+                  "OpType": "Column"
+                }
+              ],
+              "OpType": "Function"
+            },
+            {
+              "ColumnName": "dt2",
+              "OpType": "Column"
+            }
+          ],
+          "sortSpecs": [
+            {
+              "SortKey": {
+                "Signature": "ceil",
+                "FuncParams": [
+                  {
+                    "ColumnName": "dt",
+                    "OpType": "Column"
+                  }
+                ],
+                "OpType": "Function"
+              },
+              "IsAsc": true,
+              "IsNullFirst": false
+            }
+          ],
+          "windowFrame": {
+            "unit": "ROW",
+            "startBound": {
+              "boundType": "UNBOUNDED_PRECEDING"
+            },
+            "endBound": {
+              "boundType": "UNBOUNDED_FOLLOWING"
+            }
+          }
+        },
+        "IsDistinct": false,
+        "Signature": "row_number",
+        "FuncParams": [],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window8.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window8.result
new file mode 100644
index 0000000..05b95a4
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window8.result
@@ -0,0 +1,86 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "ColumnName": "dt2",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "partitionKeys": [
+            {
+              "Signature": "round",
+              "FuncParams": [
+                {
+                  "ColumnName": "dt",
+                  "OpType": "Column"
+                }
+              ],
+              "OpType": "Function"
+            },
+            {
+              "ColumnName": "dt2",
+              "OpType": "Column"
+            }
+          ],
+          "sortSpecs": [
+            {
+              "SortKey": {
+                "Signature": "ceil",
+                "FuncParams": [
+                  {
+                    "ColumnName": "dt",
+                    "OpType": "Column"
+                  }
+                ],
+                "OpType": "Function"
+              },
+              "IsAsc": true,
+              "IsNullFirst": false
+            }
+          ],
+          "windowFrame": {
+            "unit": "ROW",
+            "startBound": {
+              "boundType": "PRECEDING",
+              "number": {
+                "Value": "1",
+                "ValueType": "Unsigned_Integer",
+                "OpType": "Literal"
+              }
+            },
+            "endBound": {
+              "boundType": "CURRENT_ROW"
+            }
+          }
+        },
+        "IsDistinct": false,
+        "Signature": "row_number",
+        "FuncParams": [],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Relations": [
+      {
+        "TableName": "sum_example",
+        "OpType": "Relation"
+      }
+    ],
+    "OpType": "RelationList"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSQLAnalyzer/window9.result b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window9.result
new file mode 100644
index 0000000..d1f52dc
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestSQLAnalyzer/window9.result
@@ -0,0 +1,56 @@
+{
+  "IsDistinct": false,
+  "Projections": [
+    {
+      "Expr": {
+        "ColumnName": "dt",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "ColumnName": "dt2",
+        "OpType": "Column"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "windowName": "window1"
+        },
+        "IsDistinct": false,
+        "Signature": "row_number",
+        "FuncParams": [],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    },
+    {
+      "Expr": {
+        "WindowSpec": {
+          "windowName": "window1"
+        },
+        "IsDistinct": false,
+        "Signature": "rank",
+        "FuncParams": [],
+        "OpType": "WindowFunction"
+      },
+      "OpType": "Target"
+    }
+  ],
+  "Expr": {
+    "Expr": {
+      "Relations": [
+        {
+          "TableName": "sum_example",
+          "OpType": "Relation"
+        }
+      ],
+      "OpType": "RelationList"
+    },
+    "OpType": "Window"
+  },
+  "OpType": "Projection"
+}
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testSumFloatOverflow.result b/tajo-core/src/test/resources/results/TestSelectQuery/testSumFloatOverflow.result
index 13b9ef4..a25bbbc 100644
--- a/tajo-core/src/test/resources/results/TestSelectQuery/testSumFloatOverflow.result
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testSumFloatOverflow.result
@@ -1,3 +1,3 @@
-?sum
+?sum_1
 -------------------------------
 6.838452478692677E38
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestSelectQuery/testSumIntOverflow.result b/tajo-core/src/test/resources/results/TestSelectQuery/testSumIntOverflow.result
index cf2e0a8..f288f86 100644
--- a/tajo-core/src/test/resources/results/TestSelectQuery/testSumIntOverflow.result
+++ b/tajo-core/src/test/resources/results/TestSelectQuery/testSumIntOverflow.result
@@ -1,3 +1,3 @@
-?sum
+?sum_1
 -------------------------------
 4673934905
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber1.result b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber1.result
new file mode 100644
index 0000000..5fc49ee
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber1.result
@@ -0,0 +1,7 @@
+l_orderkey,row_num
+-------------------------------
+1,1
+1,2
+2,3
+3,4
+3,5
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber2.result b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber2.result
new file mode 100644
index 0000000..db02a76
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber2.result
@@ -0,0 +1,7 @@
+l_orderkey,row_num
+-------------------------------
+1,1
+1,2
+2,1
+3,1
+3,2
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber3.result b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber3.result
new file mode 100644
index 0000000..1d780ff
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/rowNumber3.result
@@ -0,0 +1,7 @@
+l_orderkey,row_num,l_discount,average
+-------------------------------
+1,1,0.04,0.065
+1,2,0.09,0.065
+2,1,0.0,0.0
+3,1,0.06,0.08
+3,2,0.1,0.08
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testComplexOrderBy1.result b/tajo-core/src/test/resources/results/TestWindowQuery/testComplexOrderBy1.result
new file mode 100644
index 0000000..5fc49ee
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testComplexOrderBy1.result
@@ -0,0 +1,7 @@
+l_orderkey,row_num
+-------------------------------
+1,1
+1,2
+2,3
+3,4
+3,5
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow1.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow1.result
new file mode 100644
index 0000000..f142256
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow1.result
@@ -0,0 +1,7 @@
+?windowfunction
+-------------------------------
+185.0
+185.0
+185.0
+185.0
+185.0
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow2.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow2.result
new file mode 100644
index 0000000..136d66c
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow2.result
@@ -0,0 +1,7 @@
+l_orderkey,l_quantity,?windowfunction
+-------------------------------
+1,17.0,185.0
+1,36.0,185.0
+2,38.0,185.0
+3,45.0,185.0
+3,49.0,185.0
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow3.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow3.result
new file mode 100644
index 0000000..8262c13
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow3.result
@@ -0,0 +1,7 @@
+l_orderkey,l_quantity,?windowfunction
+-------------------------------
+1,17.0,53.0
+1,36.0,53.0
+2,38.0,38.0
+3,45.0,94.0
+3,49.0,94.0
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow4.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow4.result
new file mode 100644
index 0000000..3477c5b
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow4.result
@@ -0,0 +1,7 @@
+l_orderkey,l_discount,?windowfunction,?windowfunction_1
+-------------------------------
+1,0.04,0.13,53.0
+1,0.09,0.13,53.0
+2,0.0,0.0,38.0
+3,0.06,0.16,94.0
+3,0.1,0.16,94.0
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow5.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow5.result
new file mode 100644
index 0000000..e42b1d3
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow5.result
@@ -0,0 +1,7 @@
+l_orderkey,?windowfunction,l_discount,?windowfunction_1
+-------------------------------
+1,0.13,0.04,53.0
+1,0.13,0.09,53.0
+2,0.0,0.0,38.0
+3,0.16,0.06,94.0
+3,0.16,0.1,94.0
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow6.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow6.result
new file mode 100644
index 0000000..7cf7ae1
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow6.result
@@ -0,0 +1,7 @@
+l_orderkey,l_discount,r1,r2
+-------------------------------
+1,0.04,1,0.13
+1,0.09,2,0.13
+2,0.0,1,0.0
+3,0.06,1,0.16
+3,0.1,2,0.16
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindow7.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow7.result
new file mode 100644
index 0000000..0a7de1a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindow7.result
@@ -0,0 +1,7 @@
+l_orderkey,l_quantity,r
+-------------------------------
+1,17.0,1
+1,36.0,1
+2,38.0,1
+3,45.0,1
+3,49.0,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowBeforeLimit.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowBeforeLimit.result
new file mode 100644
index 0000000..b01d89a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowBeforeLimit.result
@@ -0,0 +1,5 @@
+r_name,ran
+-------------------------------
+AFRICA,1
+AMERICA,2
+ASIA,3
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation1.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation1.result
new file mode 100644
index 0000000..7c5b94d
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation1.result
@@ -0,0 +1,3 @@
+row_num
+-------------------------------
+1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation2.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation2.result
new file mode 100644
index 0000000..ba11e16
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation2.result
@@ -0,0 +1,5 @@
+l_orderkey,row_num
+-------------------------------
+1,1
+2,1
+3,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation3.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation3.result
new file mode 100644
index 0000000..a8b99f6
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation3.result
@@ -0,0 +1,3 @@
+cnt,row_num
+-------------------------------
+5,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation4.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation4.result
new file mode 100644
index 0000000..c34577c
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation4.result
@@ -0,0 +1,5 @@
+l_orderkey,cnt,row_num
+-------------------------------
+3,2,1
+1,2,2
+2,1,3
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation5.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation5.result
new file mode 100644
index 0000000..23ff045
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation5.result
@@ -0,0 +1,5 @@
+l_orderkey,cnt,row_num
+-------------------------------
+1,2,1
+2,1,1
+3,2,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation6.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation6.result
new file mode 100644
index 0000000..7dd39ac
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithAggregation6.result
@@ -0,0 +1,5 @@
+l_orderkey,cnt,row_num
+-------------------------------
+1,2,2
+2,1,3
+3,2,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy1.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy1.result
new file mode 100644
index 0000000..a44b4e0
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy1.result
@@ -0,0 +1,7 @@
+l_orderkey,l_discount,r1
+-------------------------------
+2,0.0,1
+1,0.04,2
+3,0.06,3
+1,0.09,4
+3,0.1,5
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy2.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy2.result
new file mode 100644
index 0000000..b87197a
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy2.result
@@ -0,0 +1,7 @@
+l_orderkey,l_partkey,r1
+-------------------------------
+1,1,1
+1,1,1
+2,2,1
+3,2,1
+3,3,2
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy3.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy3.result
new file mode 100644
index 0000000..68d0f85
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy3.result
@@ -0,0 +1,7 @@
+l_orderkey,l_partkey,r1
+-------------------------------
+1,1,1
+1,1,1
+2,2,1
+3,3,1
+3,2,2
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy4.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy4.result
new file mode 100644
index 0000000..7042fb5
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy4.result
@@ -0,0 +1,7 @@
+l_orderkey,l_partkey,r1,r2
+-------------------------------
+3,3,4,1
+2,2,3,2
+3,2,4,2
+1,1,1,4
+1,1,1,4
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy5.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy5.result
new file mode 100644
index 0000000..7042fb5
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithOrderBy5.result
@@ -0,0 +1,7 @@
+l_orderkey,l_partkey,r1,r2
+-------------------------------
+3,3,4,1
+2,2,3,2
+3,2,4,2
+1,1,1,4
+1,1,1,4
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery.result
new file mode 100644
index 0000000..bb3d8e6
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery.result
@@ -0,0 +1,7 @@
+r_name,c,ran
+-------------------------------
+AFRICA,1,1
+AMERICA,1,2
+ASIA,1,3
+EUROPE,1,4
+MIDDLE EAST,1,5
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery2.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery2.result
new file mode 100644
index 0000000..c3e96b5
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery2.result
@@ -0,0 +1,5 @@
+r_name,c,ran
+-------------------------------
+AFRICA,1,1
+AMERICA,1,1
+ASIA,1,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery3.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery3.result
new file mode 100644
index 0000000..1a91238
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery3.result
@@ -0,0 +1,7 @@
+r_name,r_regionkey,mk
+-------------------------------
+AFRICA,0,1
+AMERICA,1,1
+ASIA,2,1
+EUROPE,3,1
+MIDDLE EAST,4,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery4.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery4.result
new file mode 100644
index 0000000..1a91238
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery4.result
@@ -0,0 +1,7 @@
+r_name,r_regionkey,mk
+-------------------------------
+AFRICA,0,1
+AMERICA,1,1
+ASIA,2,1
+EUROPE,3,1
+MIDDLE EAST,4,1
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery5.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery5.result
new file mode 100644
index 0000000..7668823
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery5.result
@@ -0,0 +1,5 @@
+r_name,ran
+-------------------------------
+ASIA,3
+EUROPE,4
+MIDDLE EAST,5
\ No newline at end of file
diff --git a/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery6.result b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery6.result
new file mode 100644
index 0000000..2a20b4e
--- /dev/null
+++ b/tajo-core/src/test/resources/results/TestWindowQuery/testWindowWithSubQuery6.result
@@ -0,0 +1,3 @@
+r_name,ran
+-------------------------------
+ASIA,1
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window1.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window1.result
deleted file mode 100644
index 31ed149..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window1.result
+++ /dev/null
@@ -1,42 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window2.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window2.result
deleted file mode 100644
index 378232c..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window2.result
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "name": "dt",
-              "type": "Column"
-            }
-          ],
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window3.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window3.result
deleted file mode 100644
index c5bc47d..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window3.result
+++ /dev/null
@@ -1,54 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            }
-          ],
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window4.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window4.result
deleted file mode 100644
index 0edd7d4..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window4.result
+++ /dev/null
@@ -1,52 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "sortSpecs": [
-            {
-              "key": {
-                "name": "dt",
-                "type": "Column"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window5.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window5.result
deleted file mode 100644
index 06192ff..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window5.result
+++ /dev/null
@@ -1,81 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "name": "dt2",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            },
-            {
-              "name": "dt2",
-              "type": "Column"
-            }
-          ],
-          "sortSpecs": [
-            {
-              "key": {
-                "signature": "ceil",
-                "params": [
-                  {
-                    "name": "dt",
-                    "type": "Column"
-                  }
-                ],
-                "type": "Function"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window6.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window6.result
deleted file mode 100644
index 97bd236..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window6.result
+++ /dev/null
@@ -1,87 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "name": "dt2",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "sum",
-          "params": [
-            {
-              "name": "xy",
-              "type": "Column"
-            }
-          ],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            },
-            {
-              "name": "dt2",
-              "type": "Column"
-            }
-          ],
-          "sortSpecs": [
-            {
-              "key": {
-                "signature": "ceil",
-                "params": [
-                  {
-                    "name": "dt",
-                    "type": "Column"
-                  }
-                ],
-                "type": "Function"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "windowFrame": {
-            "unit": "ROW",
-            "startBound": {
-              "boundType": "UNBOUNDED_PRECEDING"
-            }
-          },
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window7.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window7.result
deleted file mode 100644
index 9ae01bd..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window7.result
+++ /dev/null
@@ -1,85 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "name": "dt2",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "row_number",
-          "params": [],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            },
-            {
-              "name": "dt2",
-              "type": "Column"
-            }
-          ],
-          "sortSpecs": [
-            {
-              "key": {
-                "signature": "ceil",
-                "params": [
-                  {
-                    "name": "dt",
-                    "type": "Column"
-                  }
-                ],
-                "type": "Function"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "windowFrame": {
-            "unit": "ROW",
-            "startBound": {
-              "boundType": "UNBOUNDED_PRECEDING"
-            },
-            "endBound": {
-              "boundType": "UNBOUNDED_FOLLOWING"
-            }
-          },
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window8.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window8.result
deleted file mode 100644
index b9abace..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window8.result
+++ /dev/null
@@ -1,90 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "name": "dt2",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "row_number",
-          "params": [],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            },
-            {
-              "name": "dt2",
-              "type": "Column"
-            }
-          ],
-          "sortSpecs": [
-            {
-              "key": {
-                "signature": "ceil",
-                "params": [
-                  {
-                    "name": "dt",
-                    "type": "Column"
-                  }
-                ],
-                "type": "Function"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "windowFrame": {
-            "unit": "ROW",
-            "startBound": {
-              "boundType": "PRECEDING",
-              "number": {
-                "valueType": "Unsigned_Integer",
-                "value": "1",
-                "type": "Literal"
-              }
-            },
-            "endBound": {
-              "boundType": "CURRENT_ROW"
-            }
-          },
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "relations": [
-      {
-        "tableName": "sum_example",
-        "type": "Relation"
-      }
-    ],
-    "type": "RelationList"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window9.result b/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window9.result
deleted file mode 100644
index 613fb9b..0000000
--- a/tajo-core/tajo-core-backend/src/test/resources/results/TestSQLAnalyzer/window9.result
+++ /dev/null
@@ -1,118 +0,0 @@
-{
-  "distinct": false,
-  "targets": [
-    {
-      "child": {
-        "name": "dt",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "name": "dt2",
-        "type": "Column"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "row_number",
-          "params": [],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "windowName": "window1",
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    },
-    {
-      "child": {
-        "function": {
-          "distinct": false,
-          "signature": "rank",
-          "params": [],
-          "type": "GeneralSetFunction"
-        },
-        "windowSpec": {
-          "windowName": "window1",
-          "type": "WindowSpec"
-        },
-        "type": "WindowFunction"
-      },
-      "type": "Target"
-    }
-  ],
-  "child": {
-    "definitions": [
-      {
-        "windowName": "window1",
-        "windowSpec": {
-          "partitionKeys": [
-            {
-              "signature": "round",
-              "params": [
-                {
-                  "name": "dt",
-                  "type": "Column"
-                }
-              ],
-              "type": "Function"
-            },
-            {
-              "name": "dt2",
-              "type": "Column"
-            }
-          ],
-          "sortSpecs": [
-            {
-              "key": {
-                "signature": "ceil",
-                "params": [
-                  {
-                    "name": "dt",
-                    "type": "Column"
-                  }
-                ],
-                "type": "Function"
-              },
-              "asc": true,
-              "nullFirst": false
-            }
-          ],
-          "windowFrame": {
-            "unit": "ROW",
-            "startBound": {
-              "boundType": "PRECEDING",
-              "number": {
-                "valueType": "Unsigned_Integer",
-                "value": "1",
-                "type": "Literal"
-              }
-            },
-            "endBound": {
-              "boundType": "CURRENT_ROW"
-            }
-          },
-          "type": "WindowSpec"
-        }
-      }
-    ],
-    "child": {
-      "relations": [
-        {
-          "tableName": "sum_example",
-          "type": "Relation"
-        }
-      ],
-      "type": "RelationList"
-    },
-    "type": "Window"
-  },
-  "type": "Projection"
-}
\ No newline at end of file
diff --git a/tajo-storage/src/main/java/org/apache/tajo/storage/BinarySerializerDeserializer.java b/tajo-storage/src/main/java/org/apache/tajo/storage/BinarySerializerDeserializer.java
index ed034be..42b49a8 100644
--- a/tajo-storage/src/main/java/org/apache/tajo/storage/BinarySerializerDeserializer.java
+++ b/tajo-storage/src/main/java/org/apache/tajo/storage/BinarySerializerDeserializer.java
@@ -221,7 +221,7 @@
 
   private byte[] vLongBytes = new byte[9];
 
-  public int writeVLongToByteArray(byte[] bytes, int offset, long l) {
+  public static int writeVLongToByteArray(byte[] bytes, int offset, long l) {
     if (l >= -112 && l <= 127) {
       bytes[offset] = (byte) l;
       return 1;