You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2021/11/07 09:24:45 UTC

[iotdb] branch master updated: [IOTDB-1760] Support other aggregations in group by fill (#4303)

This is an automated email from the ASF dual-hosted git repository.

jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 959da45  [IOTDB-1760] Support other aggregations in group by fill (#4303)
959da45 is described below

commit 959da45cf54a4bc4ee91bb723222c5b1798432ac
Author: CRZbulabula <33...@users.noreply.github.com>
AuthorDate: Sun Nov 7 17:24:04 2021 +0800

    [IOTDB-1760] Support other aggregations in group by fill (#4303)
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |    2 +-
 .../cluster/query/fill/ClusterLinearFill.java      |    8 +-
 .../cluster/query/fill/ClusterPreviousFill.java    |    2 +-
 .../qp/logical/crud/GroupByFillQueryOperator.java  |   13 -
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    |  101 +-
 .../dataset/groupby/GroupByEngineDataSet.java      |  159 +-
 .../query/dataset/groupby/GroupByFillDataSet.java  |  220 ---
 .../GroupByFillWithoutValueFilterDataSet.java      |  577 +++++++
 .../groupby/GroupByWithValueFilterDataSet.java     |    2 +-
 .../groupby/GroupByWithoutValueFilterDataSet.java  |    4 +-
 .../iotdb/db/query/executor/QueryRouter.java       |   59 +-
 .../apache/iotdb/db/query/executor/fill/IFill.java |   83 +-
 .../iotdb/db/query/executor/fill/LinearFill.java   |   81 +-
 .../iotdb/db/query/executor/fill/PreviousFill.java |   40 +-
 .../iotdb/db/query/executor/fill/ValueFill.java    |    4 +-
 .../iotdb/db/integration/IoTDBGroupByFillIT.java   | 1785 +++++++++++++++++---
 .../db/integration/IoTDBGroupByFillMixPathsIT.java |  294 ++++
 .../db/integration/IoTDBGroupByMonthFillIT.java    |  274 +++
 .../iotdb/db/qp/physical/PhysicalPlanTest.java     |   93 +-
 .../dataset/groupby/GroupByFillDataSetTest.java    |  170 +-
 .../apache/iotdb/tsfile/read/common/RowRecord.java |    6 +-
 21 files changed, 3221 insertions(+), 756 deletions(-)

diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 1140582..3324759 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -339,7 +339,7 @@ groupByTimeClause
     ;
 
 groupByFillClause
-    : GROUP BY LR_BRACKET timeInterval COMMA DURATION_LITERAL  RR_BRACKET
+    : GROUP BY LR_BRACKET timeInterval COMMA DURATION_LITERAL (COMMA DURATION_LITERAL)? RR_BRACKET
      FILL LR_BRACKET typeClause (COMMA typeClause)* RR_BRACKET
     ;
 
diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterLinearFill.java b/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterLinearFill.java
index ed1cd7a..2264763 100644
--- a/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterLinearFill.java
+++ b/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterLinearFill.java
@@ -42,7 +42,8 @@ public class ClusterLinearFill extends LinearFill {
       Arrays.asList(SQLConstant.MIN_TIME, SQLConstant.FIRST_VALUE);
 
   ClusterLinearFill(LinearFill fill, MetaGroupMember metaGroupMember) {
-    super(fill.getDataType(), fill.getQueryTime(), fill.getBeforeRange(), fill.getAfterRange());
+    super(
+        fill.getDataType(), fill.getQueryStartTime(), fill.getBeforeRange(), fill.getAfterRange());
     this.metaGroupMember = metaGroupMember;
     this.aggregator = new ClusterAggregator(metaGroupMember);
   }
@@ -51,8 +52,9 @@ public class ClusterLinearFill extends LinearFill {
   protected TimeValuePair calculatePrecedingPoint() {
     // calculate the preceding point can be viewed as a previous fill
     ClusterPreviousFill clusterPreviousFill =
-        new ClusterPreviousFill(dataType, queryTime, beforeRange, metaGroupMember);
-    clusterPreviousFill.configureFill(seriesPath, dataType, queryTime, deviceMeasurements, context);
+        new ClusterPreviousFill(dataType, queryStartTime, beforeRange, metaGroupMember);
+    clusterPreviousFill.configureFill(
+        seriesPath, dataType, queryStartTime, deviceMeasurements, context);
     return clusterPreviousFill.getFillResult();
   }
 
diff --git a/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterPreviousFill.java b/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterPreviousFill.java
index 548af4b..cd78770 100644
--- a/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterPreviousFill.java
+++ b/cluster/src/main/java/org/apache/iotdb/cluster/query/fill/ClusterPreviousFill.java
@@ -61,7 +61,7 @@ public class ClusterPreviousFill extends PreviousFill {
   private TimeValuePair fillResult;
 
   ClusterPreviousFill(PreviousFill fill, MetaGroupMember metaGroupMember) {
-    super(fill.getDataType(), fill.getQueryTime(), fill.getBeforeRange());
+    super(fill.getDataType(), fill.getQueryStartTime(), fill.getBeforeRange());
     this.metaGroupMember = metaGroupMember;
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/GroupByFillQueryOperator.java b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/GroupByFillQueryOperator.java
index ac621cf..e849b07 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/GroupByFillQueryOperator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/GroupByFillQueryOperator.java
@@ -19,9 +19,7 @@
 
 package org.apache.iotdb.db.qp.logical.crud;
 
-import org.apache.iotdb.db.exception.query.LogicalOperatorException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
-import org.apache.iotdb.db.qp.constant.SQLConstant;
 import org.apache.iotdb.db.qp.physical.PhysicalPlan;
 import org.apache.iotdb.db.qp.physical.crud.AlignByDevicePlan;
 import org.apache.iotdb.db.qp.physical.crud.GroupByTimeFillPlan;
@@ -31,17 +29,6 @@ import org.apache.iotdb.db.qp.strategy.PhysicalGenerator;
 public class GroupByFillQueryOperator extends GroupByQueryOperator {
 
   @Override
-  public void check() throws LogicalOperatorException {
-    super.check();
-
-    for (String aggregation : selectComponent.getAggregationFunctions()) {
-      if (!SQLConstant.LAST_VALUE.equals(aggregation)) {
-        throw new LogicalOperatorException("Group By Fill only support last_value function");
-      }
-    }
-  }
-
-  @Override
   public PhysicalPlan generatePhysicalPlan(PhysicalGenerator generator)
       throws QueryProcessException {
     return isAlignByDevice()
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
index 8fe80cb..09befa7 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java
@@ -1108,25 +1108,32 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   private void parseGroupByFillClause(IoTDBSqlParser.GroupByFillClauseContext ctx) {
     GroupByFillClauseComponent groupByFillClauseComponent = new GroupByFillClauseComponent();
     groupByFillClauseComponent.setLeftCRightO(ctx.timeInterval().LS_BRACKET() != null);
-
     // parse timeUnit
     groupByFillClauseComponent.setUnit(
-        DatetimeUtils.convertDurationStrToLong(ctx.DURATION_LITERAL().getText()));
-    groupByFillClauseComponent.setSlidingStep(groupByFillClauseComponent.getUnit());
+        parseTimeUnitOrSlidingStep(
+            ctx.DURATION_LITERAL(0).getText(), true, groupByFillClauseComponent));
+    // parse sliding step
+    if (ctx.DURATION_LITERAL().size() == 2) {
+      groupByFillClauseComponent.setSlidingStep(
+          parseTimeUnitOrSlidingStep(
+              ctx.DURATION_LITERAL(1).getText(), false, groupByFillClauseComponent));
+      if (groupByFillClauseComponent.getSlidingStep() < groupByFillClauseComponent.getUnit()) {
+        throw new SQLParserException(
+            "The third parameter sliding step shouldn't be smaller than the second parameter time interval.");
+      }
+    } else {
+      groupByFillClauseComponent.setSlidingStep(groupByFillClauseComponent.getUnit());
+      groupByFillClauseComponent.setSlidingStepByMonth(
+          groupByFillClauseComponent.isIntervalByMonth());
+    }
 
     parseTimeInterval(ctx.timeInterval(), groupByFillClauseComponent);
 
     List<IoTDBSqlParser.TypeClauseContext> list = ctx.typeClause();
     Map<TSDataType, IFill> fillTypes = new EnumMap<>(TSDataType.class);
     for (IoTDBSqlParser.TypeClauseContext typeClause : list) {
-      // group by fill doesn't support linear fill
-      if (typeClause.linearClause() != null) {
-        throw new SQLParserException("group by fill doesn't support linear fill");
-      }
-      // all type use the same fill way
       if (typeClause.ALL() != null) {
         parseAllTypeClause(typeClause, fillTypes);
-        break;
       } else {
         parsePrimitiveTypeClause(typeClause, fillTypes);
       }
@@ -1207,27 +1214,43 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   private void parseAllTypeClause(
       IoTDBSqlParser.TypeClauseContext ctx, Map<TSDataType, IFill> fillTypes) {
     IFill fill;
-    long preRange;
-    if (ctx.previousUntilLastClause() != null) {
-      if (ctx.previousUntilLastClause().DURATION_LITERAL() != null) {
-        preRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.previousUntilLastClause().DURATION_LITERAL().getText());
+    int defaultFillInterval = IoTDBDescriptor.getInstance().getConfig().getDefaultFillInterval();
+
+    if (ctx.linearClause() != null) { // linear
+      if (ctx.linearClause().DURATION_LITERAL(0) != null) {
+        String beforeStr = ctx.linearClause().DURATION_LITERAL(0).getText();
+        String afterStr = ctx.linearClause().DURATION_LITERAL(1).getText();
+        fill = new LinearFill(beforeStr, afterStr);
       } else {
-        preRange = IoTDBDescriptor.getInstance().getConfig().getDefaultFillInterval();
+        fill = new LinearFill(defaultFillInterval, defaultFillInterval);
       }
-      fill = new PreviousFill(preRange, true);
-    } else {
+    } else if (ctx.previousClause() != null) { // previous
       if (ctx.previousClause().DURATION_LITERAL() != null) {
-        preRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.previousClause().DURATION_LITERAL().getText());
+        String preRangeStr = ctx.previousClause().DURATION_LITERAL().getText();
+        fill = new PreviousFill(preRangeStr);
       } else {
-        preRange = IoTDBDescriptor.getInstance().getConfig().getDefaultFillInterval();
+        fill = new PreviousFill(defaultFillInterval);
+      }
+    } else if (ctx.specificValueClause() != null) {
+      throw new SQLParserException("fill all doesn't support value fill");
+    } else { // previous until last
+      if (ctx.previousUntilLastClause().DURATION_LITERAL() != null) {
+        String preRangeStr = ctx.previousUntilLastClause().DURATION_LITERAL().getText();
+        fill = new PreviousFill(preRangeStr, true);
+      } else {
+        fill = new PreviousFill(defaultFillInterval, true);
       }
-      fill = new PreviousFill(preRange);
     }
+
     for (TSDataType tsDataType : TSDataType.values()) {
+      if (tsDataType == TSDataType.VECTOR) {
+        // TODO: TSDataType VECTOR
+        continue;
+      }
+      if (fill instanceof LinearFill
+          && (tsDataType == TSDataType.BOOLEAN || tsDataType == TSDataType.TEXT)) {
+        continue;
+      }
       fillTypes.put(tsDataType, fill.copy());
     }
   }
@@ -1235,7 +1258,12 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   private void parsePrimitiveTypeClause(
       IoTDBSqlParser.TypeClauseContext ctx, Map<TSDataType, IFill> fillTypes) {
     TSDataType dataType = parseType(ctx.dataType.getText());
-    if (ctx.linearClause() != null && dataType == TSDataType.TEXT) {
+    if (dataType == TSDataType.VECTOR) {
+      throw new SQLParserException(String.format("type %s cannot use fill function", dataType));
+    }
+
+    if (ctx.linearClause() != null
+        && (dataType == TSDataType.TEXT || dataType == TSDataType.BOOLEAN)) {
       throw new SQLParserException(
           String.format(
               "type %s cannot use %s fill function",
@@ -1246,26 +1274,21 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
 
     if (ctx.linearClause() != null) { // linear
       if (ctx.linearClause().DURATION_LITERAL(0) != null) {
-        long beforeRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.linearClause().DURATION_LITERAL(0).getText());
-        long afterRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.linearClause().DURATION_LITERAL(1).getText());
-        fillTypes.put(dataType, new LinearFill(beforeRange, afterRange));
+        String beforeRangeStr = ctx.linearClause().DURATION_LITERAL(0).getText();
+        String afterRangeStr = ctx.linearClause().DURATION_LITERAL(1).getText();
+        LinearFill fill = new LinearFill(beforeRangeStr, afterRangeStr);
+        fillTypes.put(dataType, fill);
       } else {
         fillTypes.put(dataType, new LinearFill(defaultFillInterval, defaultFillInterval));
       }
     } else if (ctx.previousClause() != null) { // previous
       if (ctx.previousClause().DURATION_LITERAL() != null) {
-        long preRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.previousClause().DURATION_LITERAL().getText());
-        fillTypes.put(dataType, new PreviousFill(preRange));
+        String beforeStr = ctx.previousClause().DURATION_LITERAL().getText();
+        fillTypes.put(dataType, new PreviousFill(beforeStr));
       } else {
         fillTypes.put(dataType, new PreviousFill(defaultFillInterval));
       }
-    } else if (ctx.specificValueClause() != null) {
+    } else if (ctx.specificValueClause() != null) { // value
       if (ctx.specificValueClause().constant() != null) {
         fillTypes.put(
             dataType, new ValueFill(ctx.specificValueClause().constant().getText(), dataType));
@@ -1274,10 +1297,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
       }
     } else { // previous until last
       if (ctx.previousUntilLastClause().DURATION_LITERAL() != null) {
-        long preRange =
-            DatetimeUtils.convertDurationStrToLong(
-                ctx.previousUntilLastClause().DURATION_LITERAL().getText());
-        fillTypes.put(dataType, new PreviousFill(preRange, true));
+        String preRangeStr = ctx.previousUntilLastClause().DURATION_LITERAL().getText();
+        fillTypes.put(dataType, new PreviousFill(preRangeStr, true));
       } else {
         fillTypes.put(dataType, new PreviousFill(defaultFillInterval, true));
       }
diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByEngineDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByEngineDataSet.java
index 690f4a6..39aa84f 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByEngineDataSet.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByEngineDataSet.java
@@ -47,10 +47,9 @@ public abstract class GroupByEngineDataSet extends QueryDataSet {
   protected boolean hasCachedTimeInterval;
 
   protected boolean leftCRightO;
-  private boolean isIntervalByMonth = false;
-  private boolean isSlidingStepByMonth = false;
-  protected int intervalTimes;
-  private static final long MS_TO_MONTH = 30 * 86400_000L;
+  protected boolean isIntervalByMonth = false;
+  protected boolean isSlidingStepByMonth = false;
+  public static final long MS_TO_MONTH = 30 * 86400_000L;
   protected AggregateResult[] curAggregateResults;
 
   public GroupByEngineDataSet() {}
@@ -66,6 +65,84 @@ public abstract class GroupByEngineDataSet extends QueryDataSet {
     initGroupByEngineDataSetFields(context, groupByTimePlan);
   }
 
+  protected Pair<Long, Long> getFirstTimeRange() {
+    long retEndTime;
+    if (isIntervalByMonth) {
+      // calculate interval length by natural month based on startTime
+      // ie. startTIme = 1/31, interval = 1mo, curEndTime will be set to 2/29
+      retEndTime = Math.min(calcIntervalByMonth(startTime, interval), endTime);
+    } else {
+      retEndTime = Math.min(startTime + interval, endTime);
+    }
+    return new Pair<>(startTime, retEndTime);
+  }
+
+  protected Pair<Long, Long> getLastTimeRange() {
+    long retStartTime;
+    long retEndTime;
+    long queryRange = endTime - startTime;
+    long intervalNum;
+
+    if (isSlidingStepByMonth) {
+      intervalNum = (long) Math.ceil(queryRange / (double) (slidingStep * MS_TO_MONTH));
+      retStartTime = calcIntervalByMonth(startTime, intervalNum * slidingStep);
+      while (retStartTime >= endTime) {
+        intervalNum -= 1;
+        retStartTime = calcIntervalByMonth(startTime, intervalNum * slidingStep);
+      }
+    } else {
+      intervalNum = (long) Math.ceil(queryRange / (double) slidingStep);
+      retStartTime = slidingStep * (intervalNum - 1) + startTime;
+    }
+
+    if (isIntervalByMonth) {
+      // calculate interval length by natural month based on curStartTime
+      // ie. startTIme = 1/31, interval = 1mo, curEndTime will be set to 2/29
+      retEndTime = Math.min(calcIntervalByMonth(retStartTime, interval), endTime);
+    } else {
+      retEndTime = Math.min(retStartTime + interval, endTime);
+    }
+
+    return new Pair<>(retStartTime, retEndTime);
+  }
+
+  protected Pair<Long, Long> getNextTimeRange(
+      long curStartTime, boolean isAscending, boolean isInside) {
+    long retStartTime, retEndTime;
+
+    if (isAscending) {
+      if (isSlidingStepByMonth) {
+        retStartTime = calcIntervalByMonth(curStartTime, (int) (slidingStep));
+      } else {
+        retStartTime = curStartTime + slidingStep;
+      }
+      // This is an open interval , [0-100)
+      if (retStartTime >= endTime && isInside) {
+        return null;
+      }
+    } else {
+      if (isSlidingStepByMonth) {
+        retStartTime = calcIntervalByMonth(curStartTime, (int) (-slidingStep));
+      } else {
+        retStartTime = curStartTime - slidingStep;
+      }
+      if (retStartTime < startTime && isInside) {
+        return null;
+      }
+    }
+
+    if (isIntervalByMonth) {
+      retEndTime = calcIntervalByMonth(retStartTime, (int) (interval));
+    } else {
+      retEndTime = retStartTime + interval;
+    }
+    if (isInside) {
+      retEndTime = Math.min(retEndTime, endTime);
+    }
+
+    return new Pair<>(retStartTime, retEndTime);
+  }
+
   protected void initGroupByEngineDataSetFields(
       QueryContext context, GroupByTimePlan groupByTimePlan) {
     this.queryId = context.getQueryId();
@@ -82,89 +159,59 @@ public abstract class GroupByEngineDataSet extends QueryDataSet {
       interval = interval / MS_TO_MONTH;
     }
 
-    // find the startTime of the first aggregation interval
-    if (ascending) {
-      curStartTime = startTime;
-    } else {
-      long queryRange = endTime - startTime;
-      // calculate the total interval number
-      long intervalNum = (long) Math.ceil(queryRange / (double) slidingStep);
-      if (isSlidingStepByMonth) {
-        intervalTimes = (int) intervalNum - 1;
-        curStartTime = calcIntervalByMonth(intervalTimes * slidingStep / MS_TO_MONTH);
-      } else {
-        curStartTime = slidingStep * (intervalNum - 1) + startTime;
-      }
-    }
-
     if (isSlidingStepByMonth) {
       slidingStep = slidingStep / MS_TO_MONTH;
     }
 
-    if (isIntervalByMonth) {
-      // calculate interval length by natural month based on curStartTime
-      // ie. startTIme = 1/31, interval = 1mo, curEndTime will be set to 2/29
-      curEndTime = Math.min(calcIntervalByMonth(interval + slidingStep * intervalTimes), endTime);
+    // find the first aggregation interval
+    Pair<Long, Long> retTimeRange;
+    if (ascending) {
+      retTimeRange = getFirstTimeRange();
     } else {
-      curEndTime = Math.min(curStartTime + interval, endTime);
+      retTimeRange = getLastTimeRange();
     }
+    curStartTime = retTimeRange.left;
+    curEndTime = retTimeRange.right;
 
     this.hasCachedTimeInterval = true;
   }
 
   @Override
   public boolean hasNextWithoutConstraint() {
-    long curSlidingStep = slidingStep;
-    long curInterval = interval;
     // has cached
     if (hasCachedTimeInterval) {
       return true;
     }
 
-    // for group by natural months addition
-    intervalTimes += ascending ? 1 : -1;
-
-    if (ascending) {
-      if (isSlidingStepByMonth) {
-        curStartTime = calcIntervalByMonth(slidingStep * intervalTimes);
-      } else {
-        curStartTime += curSlidingStep;
-      }
-      // This is an open interval , [0-100)
-      if (curStartTime >= endTime) {
-        return false;
-      }
-    } else {
-      if (isSlidingStepByMonth) {
-        curStartTime = calcIntervalByMonth(slidingStep * intervalTimes);
-      } else {
-        curStartTime -= curSlidingStep;
-      }
-      if (curStartTime < startTime) {
-        return false;
-      }
+    // find the next aggregation interval
+    Pair<Long, Long> nextTimeRange = getNextTimeRange(curStartTime, ascending, true);
+    if (nextTimeRange == null) {
+      return false;
     }
+    curStartTime = nextTimeRange.left;
+    curEndTime = nextTimeRange.right;
 
     hasCachedTimeInterval = true;
-    if (isIntervalByMonth) {
-      curEndTime = Math.min(calcIntervalByMonth(intervalTimes * slidingStep + interval), endTime);
-    } else {
-      curEndTime = Math.min(curStartTime + curInterval, endTime);
-    }
     return true;
   }
 
   /**
-   * add natural months based on the first starttime to avoid edge cases, ie 2/28
+   * add natural months based on the startTime to avoid edge cases, ie 2/28
    *
+   * @param startTime current start time
    * @param numMonths numMonths is updated in hasNextWithoutConstraint()
-   * @return curStartTime
+   * @return nextStartTime
    */
-  public long calcIntervalByMonth(long numMonths) {
+  public long calcIntervalByMonth(long startTime, long numMonths) {
     Calendar calendar = Calendar.getInstance();
     calendar.setTimeZone(SessionManager.getInstance().getCurrSessionTimeZone());
     calendar.setTimeInMillis(startTime);
+    boolean isLastDayOfMonth =
+        calendar.get(Calendar.DAY_OF_MONTH) == calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
     calendar.add(Calendar.MONTH, (int) (numMonths));
+    if (isLastDayOfMonth) {
+      calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH));
+    }
     return calendar.getTimeInMillis();
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java
deleted file mode 100644
index 11e82cd..0000000
--- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSet.java
+++ /dev/null
@@ -1,220 +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.iotdb.db.query.dataset.groupby;
-
-import org.apache.iotdb.db.conf.IoTDBDescriptor;
-import org.apache.iotdb.db.engine.StorageEngine;
-import org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor;
-import org.apache.iotdb.db.exception.StorageEngineException;
-import org.apache.iotdb.db.exception.query.QueryProcessException;
-import org.apache.iotdb.db.metadata.PartialPath;
-import org.apache.iotdb.db.qp.physical.crud.GroupByTimeFillPlan;
-import org.apache.iotdb.db.query.context.QueryContext;
-import org.apache.iotdb.db.query.executor.LastQueryExecutor;
-import org.apache.iotdb.db.query.executor.fill.IFill;
-import org.apache.iotdb.db.query.executor.fill.PreviousFill;
-import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
-import org.apache.iotdb.tsfile.read.TimeValuePair;
-import org.apache.iotdb.tsfile.read.common.Field;
-import org.apache.iotdb.tsfile.read.common.RowRecord;
-import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
-import org.apache.iotdb.tsfile.utils.Pair;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-public class GroupByFillDataSet extends QueryDataSet {
-
-  private GroupByEngineDataSet groupByEngineDataSet;
-  private Map<TSDataType, IFill> fillTypes;
-  // the first value for each time series
-  private Object[] previousValue;
-  private long[] previousTime;
-  // last timestamp for each time series
-  private long[] lastTimeArray;
-  private TimeValuePair[] firstNotNullTV;
-  private boolean isPeekEnded = false;
-
-  public GroupByFillDataSet(
-      List<PartialPath> paths,
-      List<TSDataType> dataTypes,
-      GroupByEngineDataSet groupByEngineDataSet,
-      Map<TSDataType, IFill> fillTypes,
-      QueryContext context,
-      GroupByTimeFillPlan groupByFillPlan)
-      throws StorageEngineException, IOException, QueryProcessException {
-    super(new ArrayList<>(paths), dataTypes, groupByFillPlan.isAscending());
-    this.groupByEngineDataSet = groupByEngineDataSet;
-    this.fillTypes = fillTypes;
-    List<StorageGroupProcessor> list = StorageEngine.getInstance().mergeLock(paths);
-    try {
-      initPreviousParis(context, groupByFillPlan);
-      initLastTimeArray(context, groupByFillPlan);
-    } finally {
-      StorageEngine.getInstance().mergeUnLock(list);
-    }
-  }
-
-  private void initPreviousParis(QueryContext context, GroupByTimeFillPlan groupByFillPlan)
-      throws StorageEngineException, IOException, QueryProcessException {
-    previousValue = new Object[paths.size()];
-    previousTime = new long[paths.size()];
-    firstNotNullTV = new TimeValuePair[paths.size()];
-
-    for (int i = 0; i < paths.size(); i++) {
-      PartialPath path = (PartialPath) paths.get(i);
-      TSDataType dataType = dataTypes.get(i);
-      IFill fill;
-      if (fillTypes.containsKey(dataType)) {
-        fill =
-            new PreviousFill(
-                dataType,
-                groupByEngineDataSet.getStartTime(),
-                ((PreviousFill) fillTypes.get(dataType)).getBeforeRange(),
-                ((PreviousFill) fillTypes.get(dataType)).isUntilLast());
-      } else {
-        fill =
-            new PreviousFill(
-                dataType,
-                groupByEngineDataSet.getStartTime(),
-                IoTDBDescriptor.getInstance().getConfig().getDefaultFillInterval());
-      }
-      fill.configureFill(
-          path,
-          dataType,
-          groupByEngineDataSet.getStartTime(),
-          groupByFillPlan.getAllMeasurementsInDevice(path.getDevice()),
-          context);
-
-      firstNotNullTV[i] = fill.getFillResult();
-      TimeValuePair timeValuePair = firstNotNullTV[i];
-      previousValue[i] = null;
-      previousTime[i] = Long.MAX_VALUE;
-      if (ascending && timeValuePair != null && timeValuePair.getValue() != null) {
-        previousValue[i] = timeValuePair.getValue().getValue();
-        previousTime[i] = timeValuePair.getTimestamp();
-      }
-    }
-  }
-
-  private void initLastTimeArray(QueryContext context, GroupByTimeFillPlan groupByFillPlan)
-      throws IOException, StorageEngineException, QueryProcessException {
-    lastTimeArray = new long[paths.size()];
-    Arrays.fill(lastTimeArray, Long.MAX_VALUE);
-    List<PartialPath> seriesPaths = new ArrayList<>();
-    for (int i = 0; i < paths.size(); i++) {
-      seriesPaths.add((PartialPath) paths.get(i));
-    }
-    List<Pair<Boolean, TimeValuePair>> lastValueContainer =
-        LastQueryExecutor.calculateLastPairForSeriesLocally(
-            seriesPaths, dataTypes, context, null, groupByFillPlan.getDeviceToMeasurements());
-    for (int i = 0; i < lastValueContainer.size(); i++) {
-      if (Boolean.TRUE.equals(lastValueContainer.get(i).left)) {
-        lastTimeArray[i] = lastValueContainer.get(i).right.getTimestamp();
-      }
-    }
-  }
-
-  @Override
-  public boolean hasNextWithoutConstraint() {
-    return groupByEngineDataSet.hasNextWithoutConstraint();
-  }
-
-  @Override
-  @SuppressWarnings("squid:S3776")
-  public RowRecord nextWithoutConstraint() throws IOException {
-    RowRecord rowRecord = groupByEngineDataSet.nextWithoutConstraint();
-
-    for (int i = 0; i < paths.size(); i++) {
-      Field field = rowRecord.getFields().get(i);
-      // current group by result is null
-      if (field == null || field.getDataType() == null) {
-        TSDataType tsDataType = dataTypes.get(i);
-        // for desc query peek previous time and value
-        if (!ascending && !isPeekEnded && !canUseCacheData(rowRecord, tsDataType, i)) {
-          fillCache(i);
-        }
-
-        if (canUseCacheData(rowRecord, tsDataType, i)) {
-          rowRecord.getFields().set(i, Field.getField(previousValue[i], tsDataType));
-        }
-      } else {
-        // use now value update previous value
-        previousValue[i] = field.getObjectValue(field.getDataType());
-        previousTime[i] = rowRecord.getTimestamp();
-      }
-    }
-    return rowRecord;
-  }
-
-  private void fillCache(int i) throws IOException {
-    Pair<Long, Object> data = groupByEngineDataSet.peekNextNotNullValue(paths.get(i), i);
-    if (data == null) {
-      isPeekEnded = true;
-      previousTime[i] = Long.MIN_VALUE;
-      previousValue[i] = null;
-      if (!firstCacheIsEmpty(i)) {
-        previousValue[i] = firstNotNullTV[i].getValue().getValue();
-        previousTime[i] = firstNotNullTV[i].getTimestamp();
-      }
-    } else {
-      previousValue[i] = data.right;
-      previousTime[i] = data.left;
-    }
-  }
-
-  // the previous value is not null
-  // and (fill type is not previous until last or now time is before last time)
-  // and (previous before range is not limited or previous before range contains the previous
-  // interval)
-  private boolean canUseCacheData(RowRecord rowRecord, TSDataType tsDataType, int i) {
-    PreviousFill previousFill = (PreviousFill) fillTypes.get(tsDataType);
-    return !cacheIsEmpty(i)
-        && satisfyTime(rowRecord, tsDataType, previousFill, lastTimeArray[i])
-        && satisfyRange(tsDataType, previousFill)
-        && isIncreasingTime(rowRecord, previousTime[i]);
-  }
-
-  private boolean isIncreasingTime(RowRecord rowRecord, long time) {
-    return rowRecord.getTimestamp() >= time;
-  }
-
-  private boolean satisfyTime(
-      RowRecord rowRecord, TSDataType tsDataType, PreviousFill previousFill, long lastTime) {
-    return (fillTypes.containsKey(tsDataType) && !previousFill.isUntilLast())
-        || rowRecord.getTimestamp() <= lastTime;
-  }
-
-  private boolean satisfyRange(TSDataType tsDataType, PreviousFill previousFill) {
-    return !fillTypes.containsKey(tsDataType)
-        || previousFill.getBeforeRange() < 0
-        || previousFill.getBeforeRange() >= groupByEngineDataSet.interval;
-  }
-
-  private boolean cacheIsEmpty(int i) {
-    return previousValue[i] == null;
-  }
-
-  private boolean firstCacheIsEmpty(int i) {
-    return firstNotNullTV[i] == null || firstNotNullTV[i].getValue() == null;
-  }
-}
diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillWithoutValueFilterDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillWithoutValueFilterDataSet.java
new file mode 100644
index 0000000..2eab664
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillWithoutValueFilterDataSet.java
@@ -0,0 +1,577 @@
+/*
+ * 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.iotdb.db.query.dataset.groupby;
+
+import org.apache.iotdb.db.engine.StorageEngine;
+import org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor;
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.exception.query.UnSupportedFillTypeException;
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.physical.crud.GroupByTimeFillPlan;
+import org.apache.iotdb.db.query.aggregation.AggregateResult;
+import org.apache.iotdb.db.query.aggregation.impl.CountAggrResult;
+import org.apache.iotdb.db.query.context.QueryContext;
+import org.apache.iotdb.db.query.executor.fill.IFill;
+import org.apache.iotdb.db.query.executor.fill.LinearFill;
+import org.apache.iotdb.db.query.executor.fill.PreviousFill;
+import org.apache.iotdb.db.query.executor.fill.ValueFill;
+import org.apache.iotdb.db.query.factory.AggregateResultFactory;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.TimeValuePair;
+import org.apache.iotdb.tsfile.read.common.Path;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.iotdb.tsfile.read.filter.GroupByFilter;
+import org.apache.iotdb.tsfile.read.filter.basic.Filter;
+import org.apache.iotdb.tsfile.utils.Pair;
+import org.apache.iotdb.tsfile.utils.TsPrimitiveType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+public class GroupByFillWithoutValueFilterDataSet extends GroupByWithoutValueFilterDataSet {
+
+  private static final Logger logger =
+      LoggerFactory.getLogger(GroupByFillWithoutValueFilterDataSet.class);
+
+  private Map<TSDataType, IFill> fillTypes;
+  private final List<PartialPath> deduplicatedPaths;
+  private final List<String> aggregations;
+  private Map<PartialPath, GroupByExecutor> extraPreviousExecutors = null;
+  private Map<PartialPath, GroupByExecutor> extraNextExecutors = null;
+
+  // the extra previous means first not null value before startTime
+  // used to fill result before the first not null data
+  private Object[] extraPreviousValues;
+  private long[] extraPreviousTimes;
+
+  // the previous value for each time series, which means
+  // first not null value GEQ curStartTime in order asc
+  // second not null value GEQ curStartTime in order desc
+  private Object[] previousValues;
+  private long[] previousTimes;
+
+  // the extra next means first not null value after endTime
+  // used to fill result after the last not null data
+  private Object[] extraNextValues;
+  private long[] extraNextTimes;
+
+  // the next value for each time series, which means
+  // first not null value LEQ curStartTime in order desc
+  // second not null value LEQ curStartTime in order asc
+  private Object[] nextValues;
+  private long[] nextTimes;
+
+  // the result datatype for each time series
+  private TSDataType[] resultDataType;
+
+  // the next query time range of each path
+  private long[] queryStartTimes;
+  private long[] queryEndTimes;
+  private boolean[] hasCachedQueryInterval;
+
+  public GroupByFillWithoutValueFilterDataSet(
+      QueryContext context, GroupByTimeFillPlan groupByTimeFillPlan)
+      throws QueryProcessException, StorageEngineException {
+    super(context, groupByTimeFillPlan);
+    this.aggregations = groupByTimeFillPlan.getDeduplicatedAggregations();
+
+    this.deduplicatedPaths = new ArrayList<>();
+    for (Path path : paths) {
+      PartialPath partialPath = (PartialPath) path;
+      if (!deduplicatedPaths.contains(partialPath)) {
+        deduplicatedPaths.add(partialPath);
+      }
+    }
+
+    initArrays();
+    initExtraExecutors(context, groupByTimeFillPlan);
+    if (extraPreviousExecutors != null) {
+      initExtraArrays(extraPreviousValues, extraPreviousTimes, true, extraPreviousExecutors);
+    }
+    if (extraNextExecutors != null) {
+      initExtraArrays(extraNextValues, extraNextTimes, false, extraNextExecutors);
+    }
+    initCachedTimesAndValues();
+  }
+
+  private void initArrays() {
+    extraPreviousValues = new Object[aggregations.size()];
+    extraPreviousTimes = new long[aggregations.size()];
+    previousValues = new Object[aggregations.size()];
+    previousTimes = new long[aggregations.size()];
+    extraNextValues = new Object[aggregations.size()];
+    extraNextTimes = new long[aggregations.size()];
+    nextValues = new Object[aggregations.size()];
+    nextTimes = new long[aggregations.size()];
+    Arrays.fill(extraPreviousValues, null);
+    Arrays.fill(extraPreviousTimes, Long.MIN_VALUE);
+    Arrays.fill(previousValues, null);
+    Arrays.fill(previousTimes, Long.MIN_VALUE);
+    Arrays.fill(extraNextValues, null);
+    Arrays.fill(extraNextTimes, Long.MAX_VALUE);
+    Arrays.fill(nextValues, null);
+    Arrays.fill(nextTimes, Long.MAX_VALUE);
+
+    queryStartTimes = new long[paths.size()];
+    queryEndTimes = new long[paths.size()];
+    hasCachedQueryInterval = new boolean[paths.size()];
+    resultDataType = new TSDataType[aggregations.size()];
+    Arrays.fill(queryStartTimes, curStartTime);
+    Arrays.fill(queryEndTimes, curEndTime);
+    Arrays.fill(hasCachedQueryInterval, true);
+    for (PartialPath deduplicatedPath : deduplicatedPaths) {
+      List<Integer> indexes = resultIndexes.get(deduplicatedPath);
+      for (int index : indexes) {
+        switch (aggregations.get(index)) {
+          case "avg":
+          case "sum":
+            resultDataType[index] = TSDataType.DOUBLE;
+            break;
+          case "count":
+          case "max_time":
+          case "min_time":
+            resultDataType[index] = TSDataType.INT64;
+            break;
+          case "first_value":
+          case "last_value":
+          case "max_value":
+          case "min_value":
+            resultDataType[index] = dataTypes.get(index);
+            break;
+        }
+      }
+    }
+  }
+
+  private void getGroupByExecutors(
+      Map<PartialPath, GroupByExecutor> extraExecutors,
+      QueryContext context,
+      GroupByTimeFillPlan groupByTimeFillPlan,
+      Filter timeFilter,
+      boolean isAscending)
+      throws StorageEngineException, QueryProcessException {
+    List<StorageGroupProcessor> list =
+        StorageEngine.getInstance()
+            .mergeLock(paths.stream().map(p -> (PartialPath) p).collect(Collectors.toList()));
+    try {
+      // init resultIndexes, group result indexes by path
+      for (int i = 0; i < paths.size(); i++) {
+        PartialPath path = (PartialPath) paths.get(i);
+        if (!extraExecutors.containsKey(path)) {
+          // init GroupByExecutor
+          extraExecutors.put(
+              path,
+              getGroupByExecutor(
+                  path,
+                  groupByTimeFillPlan.getAllMeasurementsInDevice(path.getDevice()),
+                  dataTypes.get(i),
+                  context,
+                  timeFilter.copy(),
+                  null,
+                  isAscending));
+        }
+        AggregateResult aggrResult =
+            AggregateResultFactory.getAggrResultByName(
+                groupByTimeFillPlan.getDeduplicatedAggregations().get(i),
+                dataTypes.get(i),
+                ascending);
+        extraExecutors.get(path).addAggregateResult(aggrResult);
+      }
+    } finally {
+      StorageEngine.getInstance().mergeUnLock(list);
+    }
+  }
+
+  /* Init extra path executors to query data outside the original group by query */
+  private void initExtraExecutors(QueryContext context, GroupByTimeFillPlan groupByTimeFillPlan)
+      throws StorageEngineException, QueryProcessException {
+    long minQueryStartTime = Long.MAX_VALUE;
+    long maxQueryEndTime = Long.MIN_VALUE;
+    this.fillTypes = groupByTimeFillPlan.getFillType();
+    for (Map.Entry<TSDataType, IFill> IFillEntry : fillTypes.entrySet()) {
+      IFill fill = IFillEntry.getValue();
+      if (fill instanceof PreviousFill) {
+        fill.convertRange(startTime, endTime);
+        minQueryStartTime = Math.min(minQueryStartTime, fill.getQueryStartTime());
+      } else if (fill instanceof LinearFill) {
+        fill.convertRange(startTime, endTime);
+        minQueryStartTime = Math.min(minQueryStartTime, fill.getQueryStartTime());
+        maxQueryEndTime = Math.max(maxQueryEndTime, fill.getQueryEndTime());
+      }
+    }
+
+    if (minQueryStartTime < Long.MAX_VALUE) {
+      extraPreviousExecutors = new HashMap<>();
+
+      long queryRange = minQueryStartTime - startTime;
+      long extraStartTime, intervalNum;
+      if (isSlidingStepByMonth) {
+        intervalNum = (long) Math.ceil(queryRange / (double) (slidingStep * MS_TO_MONTH));
+        extraStartTime = calcIntervalByMonth(startTime, intervalNum * slidingStep);
+        while (extraStartTime < minQueryStartTime) {
+          intervalNum += 1;
+          extraStartTime = calcIntervalByMonth(startTime, intervalNum * slidingStep);
+        }
+      } else {
+        intervalNum = (long) Math.ceil(queryRange / (double) slidingStep);
+        extraStartTime = slidingStep * intervalNum + startTime;
+      }
+
+      Filter timeFilter = new GroupByFilter(interval, slidingStep, extraStartTime, startTime);
+      getGroupByExecutors(extraPreviousExecutors, context, groupByTimeFillPlan, timeFilter, false);
+    }
+
+    if (maxQueryEndTime > Long.MIN_VALUE) {
+      extraNextExecutors = new HashMap<>();
+      Pair<Long, Long> lastTimeRange = getLastTimeRange();
+      lastTimeRange = getNextTimeRange(lastTimeRange.left, true, false);
+      Filter timeFilter =
+          new GroupByFilter(interval, slidingStep, lastTimeRange.left, maxQueryEndTime);
+      getGroupByExecutors(extraNextExecutors, context, groupByTimeFillPlan, timeFilter, true);
+    }
+  }
+
+  /* check if specified path has next extra range */
+  private boolean pathHasExtra(int pathId, boolean isExtraPrevious, long extraStartTime) {
+    List<Integer> Indexes = resultIndexes.get(deduplicatedPaths.get(pathId));
+    for (int resultIndex : Indexes) {
+      if (isExtraPrevious && extraPreviousValues[resultIndex] != null) {
+        continue;
+      } else if (!isExtraPrevious && extraNextValues[resultIndex] != null) {
+        continue;
+      }
+
+      IFill fill = fillTypes.get(resultDataType[resultIndex]);
+      if (fill == null) {
+        continue;
+      }
+      if (fill instanceof PreviousFill && isExtraPrevious) {
+        if (fill.getQueryStartTime() <= extraStartTime) {
+          return true;
+        }
+      } else if (fill instanceof LinearFill) {
+        if (isExtraPrevious) {
+          if (fill.getQueryStartTime() <= extraStartTime) {
+            return true;
+          }
+        } else {
+          if (extraStartTime < fill.getQueryEndTime()) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  private void initExtraArrays(
+      Object[] extraValues,
+      long[] extraTimes,
+      boolean isExtraPrevious,
+      Map<PartialPath, GroupByExecutor> extraExecutors)
+      throws QueryProcessException {
+    for (int pathId = 0; pathId < deduplicatedPaths.size(); pathId++) {
+      GroupByExecutor executor = extraExecutors.get(deduplicatedPaths.get(pathId));
+      List<Integer> Indexes = resultIndexes.get(deduplicatedPaths.get(pathId));
+
+      Pair<Long, Long> extraTimeRange;
+      if (isExtraPrevious) {
+        extraTimeRange = getFirstTimeRange();
+      } else {
+        extraTimeRange = getLastTimeRange();
+      }
+
+      extraTimeRange = getNextTimeRange(extraTimeRange.left, !isExtraPrevious, false);
+      try {
+        while (pathHasExtra(pathId, isExtraPrevious, extraTimeRange.left)) {
+          List<AggregateResult> aggregations =
+              executor.calcResult(extraTimeRange.left, extraTimeRange.right);
+          if (!resultIsNull(aggregations)) {
+            // we check extra time range in single path together,
+            // thus the extra result will be cached together
+            for (int i = 0; i < aggregations.size(); i++) {
+              if (extraValues[Indexes.get(i)] == null) {
+                extraValues[Indexes.get(i)] = aggregations.get(i).getResult();
+                extraTimes[Indexes.get(i)] = extraTimeRange.left;
+              }
+            }
+          }
+
+          extraTimeRange = getNextTimeRange(extraTimeRange.left, !isExtraPrevious, false);
+        }
+      } catch (IOException e) {
+        throw new QueryProcessException(e.getMessage());
+      }
+    }
+  }
+
+  private boolean pathHasNext(int pathId) {
+    // has cached
+    if (hasCachedQueryInterval[pathId]) {
+      return true;
+    }
+
+    // find the next aggregation interval
+    Pair<Long, Long> nextTimeRange = getNextTimeRange(queryStartTimes[pathId], ascending, true);
+    if (nextTimeRange == null) {
+      return false;
+    }
+    queryStartTimes[pathId] = nextTimeRange.left;
+    queryEndTimes[pathId] = nextTimeRange.right;
+
+    hasCachedQueryInterval[pathId] = true;
+    return true;
+  }
+
+  /* If result is null or CountAggrResult is 0, then result is NULL */
+  private boolean resultIsNull(List<AggregateResult> aggregateResults) {
+    AggregateResult result = aggregateResults.get(0);
+    if (result.getResult() == null) {
+      return true;
+    } else {
+      return result instanceof CountAggrResult && (long) result.getResult() == 0;
+    }
+  }
+
+  private void pathGetNext(int pathId) throws IOException {
+    GroupByExecutor executor = pathExecutors.get(deduplicatedPaths.get(pathId));
+    List<Integer> resultIndex = resultIndexes.get(deduplicatedPaths.get(pathId));
+
+    // Slide value and time
+    pathSlideNext(pathId);
+
+    List<AggregateResult> aggregations;
+    try {
+      // get second not null aggregate results
+      aggregations = executor.calcResult(queryStartTimes[pathId], queryEndTimes[pathId]);
+      hasCachedQueryInterval[pathId] = false;
+      while (resultIsNull(aggregations) && pathHasNext(pathId)) {
+        aggregations = executor.calcResult(queryStartTimes[pathId], queryEndTimes[pathId]);
+        hasCachedQueryInterval[pathId] = false;
+      }
+    } catch (QueryProcessException e) {
+      logger.error("GroupByFillWithoutValueFilterDataSet execute has error: ", e);
+      throw new IOException(e.getMessage(), e);
+    }
+
+    if (resultIsNull(aggregations)) {
+      pathSlide(pathId);
+    } else {
+      for (int i = 0; i < aggregations.size(); i++) {
+        int Index = resultIndex.get(i);
+        if (ascending) {
+          nextValues[Index] = aggregations.get(i).getResult();
+          nextTimes[Index] = queryStartTimes[pathId];
+        } else {
+          previousValues[Index] = aggregations.get(i).getResult();
+          previousTimes[Index] = queryStartTimes[pathId];
+        }
+      }
+    }
+
+    hasCachedQueryInterval[pathId] = false;
+  }
+
+  private void pathSlideNext(int pathId) {
+    List<Integer> resultIndex = resultIndexes.get(deduplicatedPaths.get(pathId));
+    if (ascending) {
+      for (int resultId : resultIndex) {
+        previousValues[resultId] = nextValues[resultId];
+        previousTimes[resultId] = nextTimes[resultId];
+        nextValues[resultId] = null;
+        nextTimes[resultId] = Long.MAX_VALUE;
+      }
+    } else {
+      for (int resultId : resultIndex) {
+        nextValues[resultId] = previousValues[resultId];
+        nextTimes[resultId] = previousTimes[resultId];
+        previousValues[resultId] = null;
+        previousTimes[resultId] = Long.MIN_VALUE;
+      }
+    }
+  }
+
+  private void pathSlideExtra(int pathId) {
+    List<Integer> resultIndex = resultIndexes.get(deduplicatedPaths.get(pathId));
+    if (ascending) {
+      for (int Index : resultIndex) {
+        nextValues[Index] = extraNextValues[Index];
+        nextTimes[Index] = extraNextTimes[Index];
+      }
+    } else {
+      for (int Index : resultIndex) {
+        previousValues[Index] = extraPreviousValues[Index];
+        previousTimes[Index] = extraPreviousTimes[Index];
+      }
+    }
+  }
+
+  private void pathSlide(int pathId) throws IOException {
+    if (pathHasNext(pathId)) {
+      pathGetNext(pathId);
+    } else {
+      pathSlideExtra(pathId);
+    }
+  }
+
+  /* Cache the previous and next query data before group by fill query */
+  private void initCachedTimesAndValues() throws QueryProcessException {
+    for (int pathId = 0; pathId < deduplicatedPaths.size(); pathId++) {
+      try {
+        pathSlide(pathId);
+        pathSlide(pathId);
+      } catch (IOException e) {
+        throw new QueryProcessException(e.getMessage());
+      }
+    }
+  }
+
+  private void fillRecord(
+      int resultId, RowRecord record, Pair<Long, Object> beforePair, Pair<Long, Object> afterPair)
+      throws IOException {
+    // Don't fill count aggregation
+    if (Objects.equals(aggregations.get(resultId), "count")) {
+      record.addField((long) 0, TSDataType.INT64);
+      return;
+    }
+
+    IFill fill = fillTypes.get(resultDataType[resultId]);
+    if (fill == null) {
+      record.addField(null);
+      return;
+    }
+
+    if (fill instanceof PreviousFill) {
+      if (beforePair.right != null
+          && (fill.getBeforeRange() == -1
+              || fill.insideBeforeRange(beforePair.left, record.getTimestamp()))
+          && ((!((PreviousFill) fill).isUntilLast())
+              || (afterPair.right != null && afterPair.left < endTime))) {
+        record.addField(beforePair.right, resultDataType[resultId]);
+      } else {
+        record.addField(null);
+      }
+    } else if (fill instanceof LinearFill) {
+      LinearFill linearFill = new LinearFill();
+      if (beforePair.right != null
+          && afterPair.right != null
+          && (fill.getBeforeRange() == -1
+              || fill.insideBeforeRange(beforePair.left, record.getTimestamp()))
+          && (fill.getAfterRange() == -1
+              || fill.insideAfterRange(afterPair.left, record.getTimestamp()))) {
+        try {
+          TimeValuePair filledPair =
+              linearFill.averageWithTimeAndDataType(
+                  new TimeValuePair(
+                      beforePair.left,
+                      TsPrimitiveType.getByType(resultDataType[resultId], beforePair.right)),
+                  new TimeValuePair(
+                      afterPair.left,
+                      TsPrimitiveType.getByType(resultDataType[resultId], afterPair.right)),
+                  curStartTime,
+                  resultDataType[resultId]);
+          record.addField(filledPair.getValue().getValue(), resultDataType[resultId]);
+        } catch (UnSupportedFillTypeException e) {
+          record.addField(null);
+          throw new IOException(e);
+        }
+      } else {
+        record.addField(null);
+      }
+    } else if (fill instanceof ValueFill) {
+      try {
+        TimeValuePair filledPair = fill.getFillResult();
+        record.addField(filledPair.getValue().getValue(), resultDataType[resultId]);
+      } catch (QueryProcessException | StorageEngineException e) {
+        throw new IOException(e);
+      }
+    }
+  }
+
+  @Override
+  public RowRecord nextWithoutConstraint() throws IOException {
+    if (!hasCachedTimeInterval) {
+      throw new IOException(
+          "need to call hasNext() before calling next() "
+              + "in GroupByFillWithoutValueFilterDataSet.");
+    }
+    hasCachedTimeInterval = false;
+    RowRecord record = new RowRecord(curStartTime);
+
+    boolean[] pathNeedSlide = new boolean[previousTimes.length];
+    Arrays.fill(pathNeedSlide, false);
+    for (int resultId = 0; resultId < previousTimes.length; resultId++) {
+      if (previousTimes[resultId] == curStartTime) {
+        record.addField(previousValues[resultId], resultDataType[resultId]);
+        if (!ascending) {
+          pathNeedSlide[resultId] = true;
+        }
+      } else if (nextTimes[resultId] == curStartTime) {
+        record.addField(nextValues[resultId], resultDataType[resultId]);
+        if (ascending) {
+          pathNeedSlide[resultId] = true;
+        }
+      } else if (previousTimes[resultId] < curStartTime && curStartTime < nextTimes[resultId]) {
+        fillRecord(
+            resultId,
+            record,
+            new Pair<>(previousTimes[resultId], previousValues[resultId]),
+            new Pair<>(nextTimes[resultId], nextValues[resultId]));
+      } else if (curStartTime < previousTimes[resultId]) {
+        fillRecord(
+            resultId,
+            record,
+            new Pair<>(extraPreviousTimes[resultId], extraPreviousValues[resultId]),
+            new Pair<>(previousTimes[resultId], previousValues[resultId]));
+      } else if (nextTimes[resultId] < curStartTime) {
+        fillRecord(
+            resultId,
+            record,
+            new Pair<>(nextTimes[resultId], nextValues[resultId]),
+            new Pair<>(extraNextTimes[resultId], extraNextValues[resultId]));
+      }
+    }
+
+    // Slide paths
+    // the aggregation results of one path are either all null or all not null,
+    // thus slide all results together
+    for (int pathId = 0; pathId < deduplicatedPaths.size(); pathId++) {
+      List<Integer> resultIndex = resultIndexes.get(deduplicatedPaths.get(pathId));
+      if (pathNeedSlide[resultIndex.get(0)]) {
+        pathSlide(pathId);
+      }
+    }
+
+    if (!leftCRightO) {
+      record.setTimestamp(curEndTime - 1);
+    }
+    return record;
+  }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithValueFilterDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithValueFilterDataSet.java
index 2d136da..a02b662 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithValueFilterDataSet.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithValueFilterDataSet.java
@@ -125,7 +125,7 @@ public class GroupByWithValueFilterDataSet extends GroupByEngineDataSet {
   public RowRecord nextWithoutConstraint() throws IOException {
     if (!hasCachedTimeInterval) {
       throw new IOException(
-          "need to call hasNext() before calling next()" + " in GroupByWithoutValueFilterDataSet.");
+          "need to call hasNext() before calling next()" + " in GroupByWithValueFilterDataSet.");
     }
     hasCachedTimeInterval = false;
     curAggregateResults = new AggregateResult[paths.size()];
diff --git a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithoutValueFilterDataSet.java b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithoutValueFilterDataSet.java
index 29a9948..3ead5da 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithoutValueFilterDataSet.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByWithoutValueFilterDataSet.java
@@ -54,7 +54,7 @@ public class GroupByWithoutValueFilterDataSet extends GroupByEngineDataSet {
   private static final Logger logger =
       LoggerFactory.getLogger(GroupByWithoutValueFilterDataSet.class);
 
-  private Map<PartialPath, GroupByExecutor> pathExecutors = new HashMap<>();
+  protected Map<PartialPath, GroupByExecutor> pathExecutors = new HashMap<>();
 
   /**
    * path -> result index for each aggregation
@@ -65,7 +65,7 @@ public class GroupByWithoutValueFilterDataSet extends GroupByEngineDataSet {
    *
    * <p>s1 -> 0, 2 s2 -> 1
    */
-  private Map<PartialPath, List<Integer>> resultIndexes = new HashMap<>();
+  protected Map<PartialPath, List<Integer>> resultIndexes = new HashMap<>();
 
   public GroupByWithoutValueFilterDataSet() {}
 
diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/QueryRouter.java b/server/src/main/java/org/apache/iotdb/db/query/executor/QueryRouter.java
index 00e23b0..2828f34 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/executor/QueryRouter.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/executor/QueryRouter.java
@@ -32,7 +32,7 @@ import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
 import org.apache.iotdb.db.query.context.QueryContext;
 import org.apache.iotdb.db.query.control.SessionManager;
 import org.apache.iotdb.db.query.dataset.groupby.GroupByEngineDataSet;
-import org.apache.iotdb.db.query.dataset.groupby.GroupByFillDataSet;
+import org.apache.iotdb.db.query.dataset.groupby.GroupByFillWithoutValueFilterDataSet;
 import org.apache.iotdb.db.query.dataset.groupby.GroupByLevelDataSet;
 import org.apache.iotdb.db.query.dataset.groupby.GroupByWithValueFilterDataSet;
 import org.apache.iotdb.db.query.dataset.groupby.GroupByWithoutValueFilterDataSet;
@@ -159,6 +159,22 @@ public class QueryRouter implements IQueryRouter {
     return new AggregationExecutor(context, aggregationPlan);
   }
 
+  private IExpression getOptimizeExpression(GroupByTimePlan groupByTimePlan)
+      throws QueryFilterOptimizationException, QueryProcessException {
+    IExpression expression = groupByTimePlan.getExpression();
+    List<PartialPath> selectedSeries = groupByTimePlan.getDeduplicatedPaths();
+    GlobalTimeExpression timeExpression = getTimeExpression(groupByTimePlan);
+
+    if (expression == null) {
+      expression = timeExpression;
+    } else {
+      expression = BinaryExpression.and(expression, timeExpression);
+    }
+
+    // optimize expression to an executable one
+    return ExpressionOptimizer.getInstance().optimize(expression, new ArrayList<>(selectedSeries));
+  }
+
   @Override
   public QueryDataSet groupBy(GroupByTimePlan groupByTimePlan, QueryContext context)
       throws QueryFilterOptimizationException, StorageEngineException, QueryProcessException,
@@ -173,19 +189,7 @@ public class QueryRouter implements IQueryRouter {
     }
 
     GroupByEngineDataSet dataSet = null;
-    IExpression expression = groupByTimePlan.getExpression();
-    List<PartialPath> selectedSeries = groupByTimePlan.getDeduplicatedPaths();
-    GlobalTimeExpression timeExpression = getTimeExpression(groupByTimePlan);
-
-    if (expression == null) {
-      expression = timeExpression;
-    } else {
-      expression = BinaryExpression.and(expression, timeExpression);
-    }
-
-    // optimize expression to an executable one
-    IExpression optimizedExpression =
-        ExpressionOptimizer.getInstance().optimize(expression, new ArrayList<>(selectedSeries));
+    IExpression optimizedExpression = getOptimizeExpression(groupByTimePlan);
     groupByTimePlan.setExpression(optimizedExpression);
 
     if (optimizedExpression.getType() == ExpressionType.GLOBAL_TIME) {
@@ -250,17 +254,22 @@ public class QueryRouter implements IQueryRouter {
 
   @Override
   public QueryDataSet groupByFill(GroupByTimeFillPlan groupByFillPlan, QueryContext context)
-      throws QueryFilterOptimizationException, StorageEngineException, QueryProcessException,
-          IOException {
-    GroupByEngineDataSet groupByEngineDataSet =
-        (GroupByEngineDataSet) groupBy(groupByFillPlan, context);
-    return new GroupByFillDataSet(
-        groupByFillPlan.getDeduplicatedPaths(),
-        groupByFillPlan.getDeduplicatedDataTypes(),
-        groupByEngineDataSet,
-        groupByFillPlan.getFillType(),
-        context,
-        groupByFillPlan);
+      throws QueryFilterOptimizationException, StorageEngineException, QueryProcessException {
+
+    GroupByEngineDataSet dataSet;
+    IExpression optimizedExpression = getOptimizeExpression(groupByFillPlan);
+    groupByFillPlan.setExpression(optimizedExpression);
+
+    if (optimizedExpression.getType() == ExpressionType.GLOBAL_TIME) {
+      dataSet = new GroupByFillWithoutValueFilterDataSet(context, groupByFillPlan);
+    } else {
+      // dataSet = new GroupByFillWithValueFilterDataSet(context, groupByFillPlan);
+      throw new QueryProcessException("Group by fill doesn't support valueFilter yet.");
+    }
+
+    // TODO: support group by level in group by fill
+
+    return dataSet;
   }
 
   @Override
diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/IFill.java b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/IFill.java
index a8472a4..47c7094 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/IFill.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/IFill.java
@@ -23,20 +23,29 @@ import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.query.context.QueryContext;
+import org.apache.iotdb.db.query.control.SessionManager;
+import org.apache.iotdb.db.query.dataset.groupby.GroupByEngineDataSet;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
 import org.apache.iotdb.tsfile.read.TimeValuePair;
 
 import java.io.IOException;
+import java.util.Calendar;
 import java.util.Set;
 
 public abstract class IFill {
 
-  protected long queryTime;
+  protected long queryStartTime;
+  protected long queryEndTime;
   protected TSDataType dataType;
 
-  public IFill(TSDataType dataType, long queryTime) {
+  protected boolean isBeforeByMonth = false;
+  protected long beforeRange = 0;
+  protected boolean isAfterByMonth = false;
+  protected long afterRange = 0;
+
+  public IFill(TSDataType dataType, long queryStartTime) {
     this.dataType = dataType;
-    this.queryTime = queryTime;
+    this.queryStartTime = queryStartTime;
   }
 
   public IFill() {}
@@ -61,13 +70,73 @@ public abstract class IFill {
     this.dataType = dataType;
   }
 
-  public void setQueryTime(long queryTime) {
-    this.queryTime = queryTime;
+  public void setQueryStartTime(long queryStartTime) {
+    this.queryStartTime = queryStartTime;
+  }
+
+  public long getQueryStartTime() {
+    return queryStartTime;
   }
 
-  public long getQueryTime() {
-    return queryTime;
+  public long getQueryEndTime() {
+    return queryEndTime;
   }
 
   abstract void constructFilter();
+
+  public boolean insideBeforeRange(long previous, long startTime) {
+    if (isBeforeByMonth) {
+      return previous
+          >= slideMonth(startTime, (int) (-beforeRange / GroupByEngineDataSet.MS_TO_MONTH));
+    } else {
+      return previous >= startTime - beforeRange;
+    }
+  }
+
+  public boolean insideAfterRange(long next, long startTime) {
+    if (isAfterByMonth) {
+      return next <= slideMonth(startTime, (int) (afterRange / GroupByEngineDataSet.MS_TO_MONTH));
+    } else {
+      return next <= startTime + afterRange;
+    }
+  }
+
+  public void convertRange(long startTime, long endTime) {
+    if (isBeforeByMonth) {
+      queryStartTime =
+          slideMonth(startTime, (int) (-beforeRange / GroupByEngineDataSet.MS_TO_MONTH));
+    } else {
+      queryStartTime = startTime - beforeRange;
+    }
+
+    if (isAfterByMonth) {
+      queryEndTime = slideMonth(endTime, (int) (afterRange / GroupByEngineDataSet.MS_TO_MONTH));
+    } else {
+      queryEndTime = endTime + afterRange;
+    }
+  }
+
+  public long getBeforeRange() {
+    return beforeRange;
+  }
+
+  public void setBeforeRange(long beforeRange) {
+    this.beforeRange = beforeRange;
+  }
+
+  public long getAfterRange() {
+    return afterRange;
+  }
+
+  public void setAfterRange(long afterRange) {
+    this.afterRange = afterRange;
+  }
+
+  protected long slideMonth(long startTime, int monthNum) {
+    Calendar calendar = Calendar.getInstance();
+    calendar.setTimeZone(SessionManager.getInstance().getCurrSessionTimeZone());
+    calendar.setTimeInMillis(startTime);
+    calendar.add(Calendar.MONTH, monthNum);
+    return calendar.getTimeInMillis();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/LinearFill.java b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/LinearFill.java
index 4c4b783..5dc293f 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/LinearFill.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/LinearFill.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.exception.query.UnSupportedFillTypeException;
 import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.utils.DatetimeUtils;
 import org.apache.iotdb.db.query.aggregation.AggregateResult;
 import org.apache.iotdb.db.query.aggregation.impl.FirstValueAggrResult;
 import org.apache.iotdb.db.query.aggregation.impl.MinTimeAggrResult;
@@ -45,8 +46,6 @@ import java.util.Set;
 public class LinearFill extends IFill {
 
   protected PartialPath seriesPath;
-  protected long beforeRange;
-  protected long afterRange;
   protected Filter beforeFilter;
   protected Filter afterFilter;
   protected QueryContext context;
@@ -58,6 +57,17 @@ public class LinearFill extends IFill {
     this.afterRange = afterRange;
   }
 
+  public LinearFill(String beforeStr, String afterStr) {
+    this.beforeRange = DatetimeUtils.convertDurationStrToLong(beforeStr);
+    this.afterRange = DatetimeUtils.convertDurationStrToLong(afterStr);
+    if (beforeStr.toLowerCase().contains("mo")) {
+      this.isBeforeByMonth = true;
+    }
+    if (afterStr.toLowerCase().contains("mo")) {
+      this.isAfterByMonth = true;
+    }
+  }
+
   /** Constructor of LinearFill. */
   public LinearFill(TSDataType dataType, long queryTime, long beforeRange, long afterRange) {
     super(dataType, queryTime);
@@ -65,25 +75,26 @@ public class LinearFill extends IFill {
     this.afterRange = afterRange;
   }
 
-  public long getBeforeRange() {
-    return beforeRange;
-  }
-
-  public void setBeforeRange(long beforeRange) {
+  public LinearFill(
+      TSDataType dataType,
+      long queryTime,
+      long beforeRange,
+      long afterRange,
+      boolean isBeforeByMonth,
+      boolean isAfterByMonth) {
+    super(dataType, queryTime);
     this.beforeRange = beforeRange;
-  }
-
-  public long getAfterRange() {
-    return afterRange;
-  }
-
-  public void setAfterRange(long afterRange) {
     this.afterRange = afterRange;
+    this.isBeforeByMonth = isBeforeByMonth;
+    this.isAfterByMonth = isAfterByMonth;
   }
 
+  public LinearFill() {}
+
   @Override
   public IFill copy() {
-    return new LinearFill(dataType, queryTime, beforeRange, afterRange);
+    return new LinearFill(
+        dataType, queryStartTime, beforeRange, afterRange, isBeforeByMonth, isAfterByMonth);
   }
 
   @Override
@@ -91,14 +102,14 @@ public class LinearFill extends IFill {
     Filter lowerBound =
         beforeRange == -1
             ? TimeFilter.gtEq(Long.MIN_VALUE)
-            : TimeFilter.gtEq(queryTime - beforeRange);
+            : TimeFilter.gtEq(queryStartTime - beforeRange);
     Filter upperBound =
         afterRange == -1
             ? TimeFilter.ltEq(Long.MAX_VALUE)
-            : TimeFilter.ltEq(queryTime + afterRange);
+            : TimeFilter.ltEq(queryStartTime + afterRange);
     // [queryTIme - beforeRange, queryTime + afterRange]
-    beforeFilter = FilterFactory.and(lowerBound, TimeFilter.ltEq(queryTime));
-    afterFilter = FilterFactory.and(TimeFilter.gtEq(queryTime), upperBound);
+    beforeFilter = FilterFactory.and(lowerBound, TimeFilter.ltEq(queryStartTime));
+    afterFilter = FilterFactory.and(TimeFilter.gtEq(queryStartTime), upperBound);
   }
 
   @Override
@@ -110,7 +121,7 @@ public class LinearFill extends IFill {
       QueryContext context) {
     this.seriesPath = path;
     this.dataType = dataType;
-    this.queryTime = queryTime;
+    this.queryStartTime = queryTime;
     this.context = context;
     this.deviceMeasurements = sensors;
     constructFilter();
@@ -124,16 +135,16 @@ public class LinearFill extends IFill {
     TimeValuePair afterPair = calculateSucceedingPoint();
 
     // no before data or has data on the query timestamp
-    if (beforePair.getValue() == null || beforePair.getTimestamp() == queryTime) {
-      beforePair.setTimestamp(queryTime);
+    if (beforePair.getValue() == null || beforePair.getTimestamp() == queryStartTime) {
+      beforePair.setTimestamp(queryStartTime);
       return beforePair;
     }
 
     // on after data or after data is out of range
     if (afterPair.getValue() == null
-        || afterPair.getTimestamp() < queryTime
-        || (afterRange != -1 && afterPair.getTimestamp() > queryTime + afterRange)) {
-      return new TimeValuePair(queryTime, null);
+        || afterPair.getTimestamp() < queryStartTime
+        || (afterRange != -1 && afterPair.getTimestamp() > queryStartTime + afterRange)) {
+      return new TimeValuePair(queryStartTime, null);
     }
 
     return average(beforePair, afterPair);
@@ -145,7 +156,13 @@ public class LinearFill extends IFill {
         QueryResourceManager.getInstance().getQueryDataSource(seriesPath, context, beforeFilter);
     LastPointReader lastReader =
         new LastPointReader(
-            seriesPath, dataType, deviceMeasurements, context, dataSource, queryTime, beforeFilter);
+            seriesPath,
+            dataType,
+            deviceMeasurements,
+            context,
+            dataSource,
+            queryStartTime,
+            beforeFilter);
 
     return lastReader.readLastPoint();
   }
@@ -189,7 +206,7 @@ public class LinearFill extends IFill {
   private TimeValuePair average(TimeValuePair beforePair, TimeValuePair afterPair)
       throws UnSupportedFillTypeException {
     double totalTimeLength = (double) afterPair.getTimestamp() - beforePair.getTimestamp();
-    double beforeTimeLength = (double) (queryTime - beforePair.getTimestamp());
+    double beforeTimeLength = (double) (queryStartTime - beforePair.getTimestamp());
     switch (dataType) {
       case INT32:
         int startIntValue = beforePair.getValue().getInt();
@@ -228,7 +245,15 @@ public class LinearFill extends IFill {
       default:
         throw new UnSupportedFillTypeException(dataType);
     }
-    beforePair.setTimestamp(queryTime);
+    beforePair.setTimestamp(queryStartTime);
     return beforePair;
   }
+
+  public TimeValuePair averageWithTimeAndDataType(
+      TimeValuePair beforePair, TimeValuePair afterPair, long queryTime, TSDataType tsDataType)
+      throws UnSupportedFillTypeException {
+    this.queryStartTime = queryTime;
+    this.dataType = tsDataType;
+    return average(beforePair, afterPair);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/PreviousFill.java b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/PreviousFill.java
index c6a9916..c8a1842 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/PreviousFill.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/PreviousFill.java
@@ -22,6 +22,7 @@ import org.apache.iotdb.db.engine.querycontext.QueryDataSource;
 import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.utils.DatetimeUtils;
 import org.apache.iotdb.db.query.context.QueryContext;
 import org.apache.iotdb.db.query.control.QueryResourceManager;
 import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
@@ -37,34 +38,51 @@ public class PreviousFill extends IFill {
 
   private PartialPath seriesPath;
   private QueryContext context;
-  private long beforeRange;
   private Set<String> allSensors;
   private Filter timeFilter;
 
   private boolean untilLast;
 
   public PreviousFill(TSDataType dataType, long queryTime, long beforeRange) {
-    this(dataType, queryTime, beforeRange, false);
+    this(dataType, queryTime, beforeRange, false, false);
   }
 
   public PreviousFill(long beforeRange) {
     this(beforeRange, false);
   }
 
+  public PreviousFill(String beforeStr) {
+    this(beforeStr, false);
+  }
+
   public PreviousFill(long beforeRange, boolean untilLast) {
     this.beforeRange = beforeRange;
     this.untilLast = untilLast;
   }
 
-  public PreviousFill(TSDataType dataType, long queryTime, long beforeRange, boolean untilLast) {
-    super(dataType, queryTime);
+  public PreviousFill(String beforeStr, boolean untilLast) {
+    this.beforeRange = DatetimeUtils.convertDurationStrToLong(beforeStr);
+    this.untilLast = untilLast;
+    if (beforeStr.toLowerCase().contains("mo")) {
+      this.isBeforeByMonth = true;
+    }
+  }
+
+  public PreviousFill(
+      TSDataType dataType,
+      long queryStartTime,
+      long beforeRange,
+      boolean untilLast,
+      boolean isBeforeByMonth) {
+    super(dataType, queryStartTime);
     this.beforeRange = beforeRange;
     this.untilLast = untilLast;
+    this.isBeforeByMonth = isBeforeByMonth;
   }
 
   @Override
   public IFill copy() {
-    return new PreviousFill(dataType, queryTime, beforeRange, untilLast);
+    return new PreviousFill(dataType, queryStartTime, beforeRange, untilLast, isBeforeByMonth);
   }
 
   @Override
@@ -72,13 +90,9 @@ public class PreviousFill extends IFill {
     Filter lowerBound =
         beforeRange == -1
             ? TimeFilter.gtEq(Long.MIN_VALUE)
-            : TimeFilter.gtEq(queryTime - beforeRange);
+            : TimeFilter.gtEq(queryStartTime - beforeRange);
     // time in [queryTime - beforeRange, queryTime]
-    timeFilter = FilterFactory.and(lowerBound, TimeFilter.ltEq(queryTime));
-  }
-
-  public long getBeforeRange() {
-    return beforeRange;
+    timeFilter = FilterFactory.and(lowerBound, TimeFilter.ltEq(queryStartTime));
   }
 
   @Override
@@ -91,7 +105,7 @@ public class PreviousFill extends IFill {
     this.seriesPath = path;
     this.dataType = dataType;
     this.context = context;
-    this.queryTime = queryTime;
+    this.queryStartTime = queryTime;
     this.allSensors = sensors;
     constructFilter();
   }
@@ -105,7 +119,7 @@ public class PreviousFill extends IFill {
     timeFilter = dataSource.updateFilterUsingTTL(timeFilter);
     LastPointReader lastReader =
         new LastPointReader(
-            seriesPath, dataType, allSensors, context, dataSource, queryTime, timeFilter);
+            seriesPath, dataType, allSensors, context, dataSource, queryStartTime, timeFilter);
 
     return lastReader.readLastPoint();
   }
diff --git a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/ValueFill.java b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/ValueFill.java
index 356e21b..e5df3cc 100644
--- a/server/src/main/java/org/apache/iotdb/db/query/executor/fill/ValueFill.java
+++ b/server/src/main/java/org/apache/iotdb/db/query/executor/fill/ValueFill.java
@@ -62,7 +62,7 @@ public class ValueFill extends IFill implements Cloneable {
       long queryTime,
       Set<String> deviceMeasurements,
       QueryContext context) {
-    this.queryTime = queryTime;
+    this.queryStartTime = queryTime;
   }
 
   @Override
@@ -74,7 +74,7 @@ public class ValueFill extends IFill implements Cloneable {
       case FLOAT:
       case DOUBLE:
       case TEXT:
-        return new TimeValuePair(queryTime, tsPrimitiveType);
+        return new TimeValuePair(queryStartTime, tsPrimitiveType);
       default:
         throw new UnSupportedDataTypeException("Unsupported data type:" + dataType);
     }
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillIT.java
index aaae8eb..d06315d 100644
--- a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillIT.java
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillIT.java
@@ -22,7 +22,6 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.qp.logical.crud.AggregationQueryOperator;
 import org.apache.iotdb.db.utils.EnvironmentUtils;
 import org.apache.iotdb.jdbc.Config;
-import org.apache.iotdb.jdbc.IoTDBSQLException;
 
 import org.junit.After;
 import org.junit.Assert;
@@ -34,8 +33,7 @@ import java.sql.DriverManager;
 import java.sql.ResultSet;
 import java.sql.Statement;
 
-import static org.apache.iotdb.db.constant.TestConstant.TIMESTAMP_STR;
-import static org.apache.iotdb.db.constant.TestConstant.last_value;
+import static org.apache.iotdb.db.constant.TestConstant.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -48,20 +46,26 @@ public class IoTDBGroupByFillIT {
         "CREATE TIMESERIES root.ln.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN",
         "CREATE TIMESERIES root.ln.wf01.wt01.temperature WITH DATATYPE=INT32, ENCODING=PLAIN",
         "CREATE TIMESERIES root.ln.wf01.wt01.hardware WITH DATATYPE=DOUBLE, ENCODING=PLAIN",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
             + "values(10, 21, false, 11.1)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
             + "values(12, 23, true, 22.3)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
             + "values(14, 25, false, 33.5)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
-            + "values(29, 26, false, 33.2)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
-            + "values(36, 29, false, 44.7)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
-            + "values(37, 30, false, 55.8)",
-        "INSERT INTO root.ln.wf01.wt01(timestamp,temperature,status, hardware) "
-            + "values(39, 40, false, 33.0)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(23, 28, true, 34.9)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(25, 23, false, 31.7)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(33, 29, false, 44.6)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(36, 24, true, 44.8)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(48, 28, false, 54.6)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(50, 30, true, 55.8)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) "
+            + "values(66, 40, false, 33.0)",
         "flush"
       };
 
@@ -81,10 +85,1108 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousTest1() {
+  public void previousLastValueTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,23", "27,23", "32,24", "37,24", "42,24", "47,30", "52,30"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousFirstValueTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,34.9", "27,34.9", "32,44.6", "37,44.6", "42,44.6", "47,54.6", "52,54.6"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous])");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousAvgTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,33.3", "27,33.3", "32,44.7", "37,44.7", "42,44.7", "47,55.2", "52,55.2"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousCountTest() {
+    String[] retArray =
+        new String[] {"17,0", "22,2", "27,0", "32,2", "37,0", "42,0", "47,2", "52,0"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousMaxTimeTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,25", "27,25", "32,36", "37,36", "42,36", "47,50", "52,50"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousMaxValueTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,28", "27,28", "32,29", "37,29", "42,29", "47,30", "52,30"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousMinTimeTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,23", "27,23", "32,33", "37,33", "42,33", "47,48", "52,48"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousMinValueTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,23", "27,23", "32,24", "37,24", "42,24", "47,28", "52,28"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousSumTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,66.6", "27,66.6", "32,89.4", "37,89.4", "42,89.4", "47,110.4", "52,110.4"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previous]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastLastValueTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,false", "27,false", "32,true", "37,true", "42,true", "47,true", "52,null"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select last_value(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(boolean[previousUntilLast])");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select last_value(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(boolean[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastFirstValueTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,34.9", "27,34.9", "32,44.6", "37,44.6", "42,44.6", "47,54.6", "52,null"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast])");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastAvgTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,33.3", "27,33.3", "32,44.7", "37,44.7", "42,44.7", "47,55.2", "52,null"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastCountTest() {
+    String[] retArray =
+        new String[] {"17,0", "22,2", "27,0", "32,2", "37,0", "42,0", "47,2", "52,0"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select count(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select count(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastMaxTimeTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,25", "27,25", "32,36", "37,36", "42,36", "47,50", "52,null"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastMaxValueTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,28", "27,28", "32,29", "37,29", "42,29", "47,30", "52,null"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastMinTimeTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,23", "27,23", "32,33", "37,33", "42,33", "47,48", "52,null"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastMinValueTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,23", "27,23", "32,24", "37,24", "42,24", "47,28", "52,null"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void previousUntilLastSumTest() {
+    String[] retArray =
+        new String[] {
+          "17,null", "22,66.6", "27,66.6", "32,89.4", "37,89.4", "42,89.4", "47,110.4", "52,null"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[previousUntilLast]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void usingLimit() {
+
+    String[] retArray = new String[] {"27,23", "32,24", "37,24", "42,24", "47,30"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[previous]) "
+                  + "limit 5 offset 2");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void valueLastValueTest() {
+    String[] retArray =
+        new String[] {"17,100", "22,23", "27,100", "32,24", "37,100", "42,100", "47,30", "52,100"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[100])");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[100]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void valueFirstValueTest() {
+    String[] retArray =
+        new String[] {
+          "17,2.33", "22,34.9", "27,2.33", "32,44.6", "37,2.33", "42,2.33", "47,54.6", "52,2.33"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[2.33])");
+
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[2.33]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void valueAvgTest() {
     String[] retArray =
         new String[] {
-          "17,25", "22,25", "27,26", "32,29", "37,40", "42,40", "47,40",
+          "17,66.6", "22,33.3", "27,66.6", "32,44.7", "37,66.6", "42,66.6", "47,55.2", "52,66.6"
         };
 
     try (Connection connection =
@@ -92,9 +1194,113 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(int32[previous])");
-
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[66.6])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[66.6]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void valueCountTest() {
+    String[] retArray =
+        new String[] {"17,0", "22,2", "27,0", "32,2", "37,0", "42,0", "47,2", "52,0"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[10])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[10]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void valueMaxTimeTest() {
+    String[] retArray =
+        new String[] {"17,888", "22,25", "27,888", "32,36", "37,888", "42,888", "47,50", "52,888"};
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[888])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -103,7 +1309,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -112,9 +1318,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(int32[previous]) order by time desc");
-
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[888]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -122,13 +1328,12 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
-
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -136,28 +1341,103 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousTest2() {
+  public void valueMaxValueTest() {
+    String[] retArray =
+        new String[] {"17,100", "22,28", "27,100", "32,29", "37,100", "42,100", "47,30", "52,100"};
+
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
-      statement.execute(
-          "select count(temperature) from root.ln.wf01.wt01 "
-              + "GROUP BY ([17, 48), 5ms) FILL(int32[previous])");
-    } catch (IoTDBSQLException e) {
-      assertTrue(e.getMessage().contains("Group By Fill only support last_value function"));
+      boolean hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[100])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[100]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
     }
+  }
+
+  @Test
+  public void valueMinTimeTest() {
+    String[] retArray =
+        new String[] {"17,1", "22,23", "27,1", "32,33", "37,1", "42,1", "47,48", "52,1"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
-      statement.execute(
-          "select count(temperature) from root.ln.wf01.wt01 "
-              + "GROUP BY ([17, 48), 5ms) FILL(int32[previous]) order by time desc");
-    } catch (IoTDBSQLException e) {
-      assertTrue(e.getMessage().contains("Group By Fill only support last_value function"));
+      boolean hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[1])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[1]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -165,20 +1445,18 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousTest3() {
+  public void valueMinValueTest() {
     String[] retArray =
-        new String[] {
-          "2,null", "7,21", "12,25", "17,25", "22,25", "27,26", "32,29", "37,40", "42,40", "47,40",
-        };
+        new String[] {"17,10", "22,23", "27,10", "32,24", "37,10", "42,10", "47,28", "52,10"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previous])");
-
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[10])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -187,7 +1465,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -196,9 +1474,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previous]) order by time desc");
-
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[10]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -206,13 +1484,12 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
-
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -220,19 +1497,17 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousTest4() {
+  public void valueSumTest() {
     String[] retArray =
         new String[] {
-          "2,null,null",
-          "7,21,11.1",
-          "12,25,33.5",
-          "17,25,33.5",
-          "22,25,33.5",
-          "27,26,33.2",
-          "32,29,44.7",
-          "37,40,33.0",
-          "42,40,33.0",
-          "47,40,33.0",
+          "17,233.0",
+          "22,66.6",
+          "27,233.0",
+          "32,89.4",
+          "37,233.0",
+          "42,233.0",
+          "47,110.4",
+          "52,233.0"
         };
 
     try (Connection connection =
@@ -240,9 +1515,9 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previous], double[previous])");
-
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[233.0])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -251,9 +1526,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -262,9 +1535,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previous], double[previous]) order by time desc");
-
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[233.0]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -272,15 +1545,12 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
-
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -288,19 +1558,18 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void leftORightCPreviousTest() {
+  public void linearLastValueTest() {
     String[] retArray =
-        new String[] {
-          "10,21", "15,25", "20,25", "25,25", "30,26", "35,26", "40,40",
-        };
+        new String[] {"17,null", "22,23", "27,23", "32,24", "37,26", "42,28", "47,30", "52,null"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ((5, 40], 5ms) FILL(int32[previous])");
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear])");
 
       assertTrue(hasResultSet);
       int cnt;
@@ -319,9 +1588,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ((5, 40], 5ms) FILL(int32[previous]) order by time desc");
-
+              "select last_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -342,19 +1611,17 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousAllTest() {
+  public void linearFirstValueTest() {
     String[] retArray =
         new String[] {
-          "2,null,null",
-          "7,21,11.1",
-          "12,25,33.5",
-          "17,25,33.5",
-          "22,25,33.5",
-          "27,26,33.2",
-          "32,29,44.7",
-          "37,40,33.0",
-          "42,40,33.0",
-          "47,40,33.0",
+          "17,null",
+          "22,34.9",
+          "27,39.75",
+          "32,44.6",
+          "37,47.93333333333334",
+          "42,51.266666666666666",
+          "47,54.6",
+          "52,null"
         };
 
     try (Connection connection =
@@ -362,8 +1629,9 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(ALL[previous])");
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear])");
 
       assertTrue(hasResultSet);
       int cnt;
@@ -373,9 +1641,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -384,9 +1650,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(ALL[previous]) order by time desc");
-
+              "select first_value(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -394,9 +1660,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
@@ -409,10 +1673,10 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastTest1() {
+  public void linearAvgTest() {
     String[] retArray =
         new String[] {
-          "17,25", "22,25", "27,26", "32,29", "37,40", "42,null", "47,null",
+          "17,null", "22,33.3", "27,39.0", "32,44.7", "37,48.2", "42,51.7", "47,55.2", "52,null"
         };
 
     try (Connection connection =
@@ -420,9 +1684,9 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(int32[previousUntilLast])");
-
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -431,17 +1695,18 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
+
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(int32[previousUntilLast]) order by time desc");
-
+              "select avg(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -449,7 +1714,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
@@ -462,30 +1727,103 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastTest2() {
+  public void linearCountTest() {
+    String[] retArray =
+        new String[] {"17,0", "22,2", "27,0", "32,2", "37,0", "42,0", "47,2", "52,0"};
+
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
-      statement.execute(
-          "select count(temperature) from root.ln.wf01.wt01 "
-              + "GROUP BY ([17, 48), 5ms) FILL(int32[previousUntilLast])");
-    } catch (IoTDBSQLException e) {
-      System.out.println(e.getMessage());
-      assertTrue(e.getMessage().contains("Group By Fill only support last_value function"));
+      boolean hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select count(status) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(count("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
     }
+  }
+
+  @Test
+  public void linearMaxTimeTest() {
+    String[] retArray =
+        new String[] {"17,null", "22,25", "27,30", "32,36", "37,40", "42,45", "47,50", "52,null"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
-      statement.execute(
-          "select count(temperature) from root.ln.wf01.wt01 "
-              + "GROUP BY ([17, 48), 5ms) FILL(int32[previousUntilLast]) order by time desc");
-    } catch (IoTDBSQLException e) {
-      System.out.println(e.getMessage());
-      assertTrue(e.getMessage().contains("Group By Fill only support last_value function"));
+      boolean hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select max_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -493,21 +1831,18 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastTest3() {
+  public void linearMaxValueTest() {
     String[] retArray =
-        new String[] {
-          "2,null", "7,21", "12,25", "17,25", "22,25", "27,26", "32,29", "37,40", "42,null",
-          "47,null",
-        };
+        new String[] {"17,null", "22,28", "27,28", "32,29", "37,29", "42,29", "47,30", "52,null"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previousUntilLast])");
-
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -516,7 +1851,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -525,9 +1860,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previousUntilLast]) order by time desc");
-
+              "select max_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -535,7 +1870,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
@@ -548,29 +1883,18 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastTest4() {
+  public void linearMinTimeTest() {
     String[] retArray =
-        new String[] {
-          "2,null,null",
-          "7,21,11.1",
-          "12,25,33.5",
-          "17,25,33.5",
-          "22,25,33.5",
-          "27,26,33.2",
-          "32,29,44.7",
-          "37,40,33.0",
-          "42,null,null",
-          "47,null,null",
-        };
+        new String[] {"17,null", "22,23", "27,28", "32,33", "37,38", "42,43", "47,48", "52,null"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previousUntilLast], double[previousUntilLast])");
-
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -579,9 +1903,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -590,9 +1912,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(int32[previousUntilLast], double[previousUntilLast]) order by time desc");
-
+              "select min_time(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int64[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -600,9 +1922,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(min_time("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
@@ -615,20 +1935,18 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastTest5() {
+  public void linearMinValueTest() {
     String[] retArray =
-        new String[] {
-          "17,25", "22,25", "27,26", "32,29", "37,40", "42,null", "47,null",
-        };
+        new String[] {"17,null", "22,23", "27,23", "32,24", "37,25", "42,26", "47,28", "52,null"};
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(float[previousUntilLast])");
-
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -637,7 +1955,7 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
@@ -646,9 +1964,9 @@ public class IoTDBGroupByFillIT {
 
       hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(float[previousUntilLast]) order by time desc");
-
+              "select min_value(temperature) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(int32[linear]) order by time desc");
       assertTrue(hasResultSet);
       try (ResultSet resultSet = statement.getResultSet()) {
         cnt = 0;
@@ -656,13 +1974,12 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[retArray.length - cnt - 1], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
-
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -670,18 +1987,20 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void leftORightCPreviousUntilLastTest() {
+  public void linearSumTest() {
     String[] retArray =
-        new String[] {"9,null", "14,25", "19,25", "24,25", "29,26", "34,26", "39,40", "44,null"};
+        new String[] {
+          "17,null", "22,66.6", "27,78.0", "32,89.4", "37,96.4", "42,103.4", "47,110.4", "52,null"
+        };
 
     try (Connection connection =
             DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ((4, 44], 5ms) FILL(int32[previousUntilLast])");
-
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear])");
       assertTrue(hasResultSet);
       int cnt;
       try (ResultSet resultSet = statement.getResultSet()) {
@@ -690,13 +2009,31 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
 
+      hasResultSet =
+          statement.execute(
+              "select sum(hardware) from "
+                  + "root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(double[linear]) order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -704,19 +2041,10 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void previousUntilLastAllTest() {
+  public void leftORightCPreviousTest() {
     String[] retArray =
         new String[] {
-          "2,null,null",
-          "7,21,11.1",
-          "12,25,33.5",
-          "17,25,33.5",
-          "22,25,33.5",
-          "27,26,33.2",
-          "32,29,44.7",
-          "37,40,33.0",
-          "42,null,null",
-          "47,null,null",
+          "10,21.0", "15,24.0", "20,24.0", "25,25.5", "30,25.5", "35,29.0", "40,24.0",
         };
 
     try (Connection connection =
@@ -724,8 +2052,8 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature), last_value(hardware) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([2, 48), 5ms) FILL(ALL[previousUntilLast])");
+              "select avg(temperature) from root.ln.wf01.wt01 "
+                  + "GROUP BY ((5, 40], 5ms) FILL(double[previous])");
 
       assertTrue(hasResultSet);
       int cnt;
@@ -735,15 +2063,31 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
-                  + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.hardware"));
+                  + resultSet.getString(avg("root.ln.wf01.wt01.temperature"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
 
+      hasResultSet =
+          statement.execute(
+              "select avg(temperature) from root.ln.wf01.wt01 "
+                  + "GROUP BY ((5, 40], 5ms) FILL(double[previous]) order by time desc");
+
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -751,11 +2095,17 @@ public class IoTDBGroupByFillIT {
   }
 
   @Test
-  public void usingLimit() {
-
+  public void previousAllTest() {
     String[] retArray =
         new String[] {
-          "27,26", "32,29", "37,40", "42,40", "47,40",
+          "17,null,null",
+          "22,false,33.3",
+          "27,false,33.3",
+          "32,true,44.7",
+          "37,true,44.7",
+          "42,true,44.7",
+          "47,true,55.2",
+          "52,true,55.2"
         };
 
     try (Connection connection =
@@ -763,9 +2113,8 @@ public class IoTDBGroupByFillIT {
         Statement statement = connection.createStatement()) {
       boolean hasResultSet =
           statement.execute(
-              "select last_value(temperature) from root.ln.wf01.wt01 "
-                  + "GROUP BY ([17, 48), 5ms) FILL(int32[previous]) "
-                  + "limit 5 offset 2");
+              "select last_value(status), avg(hardware) from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(ALL[previous])");
 
       assertTrue(hasResultSet);
       int cnt;
@@ -775,13 +2124,35 @@ public class IoTDBGroupByFillIT {
           String ans =
               resultSet.getString(TIMESTAMP_STR)
                   + ","
-                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"));
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.status"))
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
           assertEquals(retArray[cnt], ans);
           cnt++;
         }
         assertEquals(retArray.length, cnt);
       }
 
+      hasResultSet =
+          statement.execute(
+              "select last_value(status), avg(hardware) from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 55), 5ms) FILL(ALL[previous]) order by time desc");
+
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.status"))
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillMixPathsIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillMixPathsIT.java
new file mode 100644
index 0000000..7068448
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByFillMixPathsIT.java
@@ -0,0 +1,294 @@
+/*
+ * 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.iotdb.db.integration;
+
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+
+import static org.apache.iotdb.db.constant.TestConstant.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class IoTDBGroupByFillMixPathsIT {
+
+  private static String[] dataSet1 =
+      new String[] {
+        "SET STORAGE GROUP TO root.ln.wf01.wt01",
+        "CREATE TIMESERIES root.ln.wf01.wt01.temperature WITH DATATYPE=INT32, ENCODING=PLAIN",
+        "CREATE TIMESERIES root.ln.wf01.wt01.hardware WITH DATATYPE=DOUBLE, ENCODING=PLAIN",
+        "CREATE TIMESERIES root.ln.wf01.wt01.status WITH DATATYPE=BOOLEAN, ENCODING=PLAIN",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(8, 23)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, status) values(10, true)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(11, 11.0)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(23, 28)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(25, 23)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(27, 33.7)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(29, 35.3)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(30, 36.0)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) values(32, 22, false, 40.7)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) values(33, 25, false, 42.5)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) values(34, 29, false, 43.6)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) values(35, 23, false, 41.8)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status, hardware) values(36, 27, true, 48.2)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(37, 36.8)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(40, 38.2)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(41, 36.0)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, status) values(44, false)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, status) values(45, false)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(47, 35)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(48, 42)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(50, 36)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(51, 22)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status) values(52, 15, false)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status) values(53, 13, true)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status) values(54, 24, false)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status) values(55, 38, false)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature, status) values(56, 20, true)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(58, 40.5)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(60, 27.5)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(61, 36.4)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, temperature) values(72, 33)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, status) values(74, true)",
+        "INSERT INTO root.ln.wf01.wt01(timestamp, hardware) values(75, 46.8)",
+        "flush"
+      };
+
+  @Before
+  public void setUp() throws Exception {
+    EnvironmentUtils.closeStatMonitor();
+    EnvironmentUtils.envSetUp();
+    IoTDBDescriptor.getInstance().getConfig().setPartitionInterval(1000);
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    prepareData();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    IoTDBDescriptor.getInstance().getConfig().setPartitionInterval(86400);
+    EnvironmentUtils.cleanEnv();
+  }
+
+  private void prepareData() {
+    try (Connection connection =
+            DriverManager.getConnection(
+                Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      for (String sql : dataSet1) {
+        statement.execute(sql);
+      }
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Test
+  public void singlePathMixTest() {
+    String[] retArray =
+        new String[] {
+          // "7,23.0,23,8"
+          "17,41.66666666666667,23,8",
+          "22,51.0,23,25",
+          "27,88.5,25,25",
+          "32,126.0,27,36",
+          "37,129.0,26,36",
+          "42,132.0,24,36",
+          "47,135.0,22,51",
+          "52,110.0,20,56",
+          "57,null,23,null",
+          "62,71.5,26,null"
+          // "72,33.0,33,72"
+        };
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature), last_value(temperature), max_time(temperature) "
+                  + "from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 65), 5ms) "
+                  + "FILL(double[linear, 12ms, 12ms], int32[linear, 10ms, 18ms], int64[previousUntilLast, 17ms])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select sum(temperature), last_value(temperature), max_time(temperature) "
+                  + "from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 65), 5ms) "
+                  + "FILL(double[linear, 12ms, 12ms], int32[linear, 10ms, 18ms], int64[previousUntilLast, 17ms]) "
+                  + "order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(last_value("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.temperature"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void MultiPathsMixTest() {
+    String[] retArray =
+        new String[] {
+          "17,41.66666666666667,23.0,10,23,23.5,true",
+          "22,51.0,null,10,23,null,true",
+          "27,88.5,35.0,null,23,36.0,true",
+          "32,126.0,43.36,36,22,48.2,false",
+          "37,129.0,37.0,36,22,38.2,true",
+          "42,132.0,null,45,22,null,false",
+          "47,135.0,35.900000000000006,45,22,39.35,true",
+          "52,110.0,null,56,13,null,false",
+          "57,null,34.800000000000004,null,18,40.5,true",
+          "62,71.5,38.800000000000004,null,23,42.6,true"
+        };
+
+    /*  Format result
+                  linear,      linear, preUntil,   linear,       linear,      value
+          7,        23.0,        11.0,       10,       23,         11.0,       true
+         17, 41.67(null),  23.0(null), 10(null), 23(null),   23.5(null), true(null)
+         22,        51.0,        null, 10(null),       23,         null, true(null)
+         27,  88.5(null),        35.0,     null, 23(null),         36.0, true(null)
+         32,       126.0,       43.36,       36,       22,         48.2,      false
+         37, 129.0(null),        37.0, 36(null), 22(null),         38.2, true(null)
+         42, 132.0(null),        null,       45, 22(null),         null,      false
+         47,       135.0,  35.9(null), 45(null),       22,  39.35(null), true(null)
+         52,       110.0,        null,       56,       13,         null,      false
+         57,        null,        34.8,     null, 18(null),         40.5, true(null)
+         62,  71.5(null),  38.8(null),     null, 23(null),   42.6(null), true(null)
+         72,        33.0,        46.8,       74,       33,         46.8,       true
+    */
+
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature), avg(hardware), max_time(status), "
+                  + "min_value(temperature), max_value(hardware), first_value(status) "
+                  + "from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 65), 5ms) "
+                  + "FILL(double[linear, 12ms, 12ms], int32[linear, 12ms, 18ms], "
+                  + "int64[previousUntilLast, 17ms], boolean[true])");
+      assertTrue(hasResultSet);
+      int cnt;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"))
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.status"))
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.hardware"))
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[cnt], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+
+      hasResultSet =
+          statement.execute(
+              "select sum(temperature), avg(hardware), max_time(status), "
+                  + "min_value(temperature), max_value(hardware), first_value(status) "
+                  + "from root.ln.wf01.wt01 "
+                  + "GROUP BY ([17, 65), 5ms) "
+                  + "FILL(double[linear, 12ms, 12ms], int32[linear, 12ms, 18ms], "
+                  + "int64[previousUntilLast, 17ms], boolean[true]) "
+                  + "order by time desc");
+      assertTrue(hasResultSet);
+      try (ResultSet resultSet = statement.getResultSet()) {
+        cnt = 0;
+        while (resultSet.next()) {
+          String ans =
+              resultSet.getString(TIMESTAMP_STR)
+                  + ","
+                  + resultSet.getString(sum("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(avg("root.ln.wf01.wt01.hardware"))
+                  + ","
+                  + resultSet.getString(max_time("root.ln.wf01.wt01.status"))
+                  + ","
+                  + resultSet.getString(min_value("root.ln.wf01.wt01.temperature"))
+                  + ","
+                  + resultSet.getString(max_value("root.ln.wf01.wt01.hardware"))
+                  + ","
+                  + resultSet.getString(first_value("root.ln.wf01.wt01.status"));
+          assertEquals(retArray[retArray.length - cnt - 1], ans);
+          cnt++;
+        }
+        assertEquals(retArray.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+}
diff --git a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByMonthFillIT.java b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByMonthFillIT.java
new file mode 100644
index 0000000..a770460
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBGroupByMonthFillIT.java
@@ -0,0 +1,274 @@
+/*
+ * 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.iotdb.db.integration;
+
+import org.apache.iotdb.db.utils.EnvironmentUtils;
+import org.apache.iotdb.jdbc.Config;
+import org.apache.iotdb.jdbc.IoTDBConnection;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+import static org.apache.iotdb.db.constant.TestConstant.sum;
+import static org.junit.Assert.fail;
+
+public class IoTDBGroupByMonthFillIT {
+
+  private static final String TIMESTAMP_STR = "Time";
+  private final DateFormat df = new SimpleDateFormat("MM/dd/yyyy:HH:mm:ss");
+
+  @Before
+  public void setUp() throws Exception {
+    df.setTimeZone(TimeZone.getTimeZone("GMT+00:00"));
+    EnvironmentUtils.envSetUp();
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    prepareData();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    EnvironmentUtils.cleanEnv();
+  }
+
+  /** Test StartTime: 2020-02-15, EndTime: 2020-11-15 PreviousFill beforeRange = 1mo */
+  @Test
+  public void previousFillTest1() {
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      String[] retArray1 = {
+        "02/15/2020:02:00:00", "1.0",
+        "03/15/2020:02:00:00", "3.0",
+        "04/15/2020:02:00:00", "3.0",
+        "05/15/2020:02:00:00", null,
+        "06/15/2020:02:00:00", "6.0",
+        "07/15/2020:02:00:00", "6.0",
+        "08/15/2020:02:00:00", null,
+        "09/15/2020:02:00:00", "9.0",
+        "10/15/2020:02:00:00", "9.0",
+        "11/15/2020:02:00:00", null,
+      };
+
+      ((IoTDBConnection) connection).setTimeZone("GMT+00:00");
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature) from root.sg1.d1 "
+                  + "GROUP BY ([1581732000000, 1607997600000), 1mo) "
+                  + "FILL(ALL[previous, 1mo])");
+
+      Assert.assertTrue(hasResultSet);
+      int cnt = 0;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        while (resultSet.next()) {
+          String time = resultSet.getString(TIMESTAMP_STR);
+          String ans = resultSet.getString(sum("root.sg1.d1.temperature"));
+          Assert.assertEquals(retArray1[cnt++], df.format(Long.parseLong(time)));
+          Assert.assertEquals(retArray1[cnt++], ans);
+        }
+        Assert.assertEquals(retArray1.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  /** Test StartTime: 2020-02-15, EndTime: 2020-11-15 PreviousFill beforeRange = 2mo */
+  @Test
+  public void previousFillTest2() {
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      String[] retArray1 = {
+        "02/15/2020:02:00:00", "1.0",
+        "03/15/2020:02:00:00", "3.0",
+        "04/15/2020:02:00:00", "3.0",
+        "05/15/2020:02:00:00", "3.0",
+        "06/15/2020:02:00:00", "6.0",
+        "07/15/2020:02:00:00", "6.0",
+        "08/15/2020:02:00:00", "6.0",
+        "09/15/2020:02:00:00", "9.0",
+        "10/15/2020:02:00:00", "9.0",
+        "11/15/2020:02:00:00", "9.0",
+      };
+
+      ((IoTDBConnection) connection).setTimeZone("GMT+00:00");
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature) from root.sg1.d1 "
+                  + "GROUP BY ([1581732000000, 1607997600000), 1mo) "
+                  + "FILL(ALL[previous, 2mo])");
+
+      Assert.assertTrue(hasResultSet);
+      int cnt = 0;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        while (resultSet.next()) {
+          String time = resultSet.getString(TIMESTAMP_STR);
+          String ans = resultSet.getString(sum("root.sg1.d1.temperature"));
+          Assert.assertEquals(retArray1[cnt++], df.format(Long.parseLong(time)));
+          Assert.assertEquals(retArray1[cnt++], ans);
+        }
+        Assert.assertEquals(retArray1.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  /**
+   * Test StartTime: 2020-02-15, EndTime: 2020-11-15 PreviousFill beforeRange = 1mo, afterRange =
+   * 1mo
+   */
+  @Test
+  public void LinearFillTest1() {
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      String[] retArray1 = {
+        "02/15/2020:02:00:00", "2.0",
+        "03/15/2020:02:00:00", "3.0",
+        "04/15/2020:02:00:00", null,
+        "05/15/2020:02:00:00", null,
+        "06/15/2020:02:00:00", "6.0",
+        "07/15/2020:02:00:00", null,
+        "08/15/2020:02:00:00", null,
+        "09/15/2020:02:00:00", "9.0",
+        "10/15/2020:02:00:00", null,
+        "11/15/2020:02:00:00", null,
+      };
+
+      ((IoTDBConnection) connection).setTimeZone("GMT+00:00");
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature) from root.sg1.d1 "
+                  + "GROUP BY ([1581732000000, 1607997600000), 1mo) "
+                  + "FILL(ALL[linear, 1mo, 1mo])");
+
+      Assert.assertTrue(hasResultSet);
+      int cnt = 0;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        while (resultSet.next()) {
+          String time = resultSet.getString(TIMESTAMP_STR);
+          String ans = resultSet.getString(sum("root.sg1.d1.temperature"));
+          Assert.assertEquals(retArray1[cnt++], df.format(Long.parseLong(time)));
+          if (retArray1[cnt] == null) {
+            Assert.assertNull(ans);
+          } else {
+            Assert.assertEquals(Double.valueOf(retArray1[cnt]), Double.valueOf(ans), 0.1);
+          }
+          ++cnt;
+        }
+        Assert.assertEquals(retArray1.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  /**
+   * Test StartTime: 2020-02-15, EndTime: 2020-11-15 PreviousFill beforeRange = 2mo, afterRange =
+   * 2mo
+   */
+  @Test
+  public void LinearFillTest2() {
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      String[] retArray1 = {
+        "02/15/2020:02:00:00", "2.0",
+        "03/15/2020:02:00:00", "3.0",
+        "04/15/2020:02:00:00", "4.0",
+        "05/15/2020:02:00:00", "5.0",
+        "06/15/2020:02:00:00", "6.0",
+        "07/15/2020:02:00:00", "7.0",
+        "08/15/2020:02:00:00", "8.0",
+        "09/15/2020:02:00:00", "9.0",
+        "10/15/2020:02:00:00", "10.0",
+        "11/15/2020:02:00:00", "11.0",
+      };
+
+      ((IoTDBConnection) connection).setTimeZone("GMT+00:00");
+      boolean hasResultSet =
+          statement.execute(
+              "select sum(temperature) from root.sg1.d1 "
+                  + "GROUP BY ([1581732000000, 1607997600000), 1mo) "
+                  + "FILL(ALL[linear, 2mo, 2mo])");
+
+      Assert.assertTrue(hasResultSet);
+      int cnt = 0;
+      try (ResultSet resultSet = statement.getResultSet()) {
+        while (resultSet.next()) {
+          String time = resultSet.getString(TIMESTAMP_STR);
+          String ans = resultSet.getString(sum("root.sg1.d1.temperature"));
+          Assert.assertEquals(retArray1[cnt++], df.format(Long.parseLong(time)));
+          Assert.assertEquals(Double.valueOf(retArray1[cnt++]), Double.valueOf(ans), 0.1);
+        }
+        Assert.assertEquals(retArray1.length, cnt);
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  private void prepareData() {
+    try (Connection connection =
+            DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", "root");
+        Statement statement = connection.createStatement()) {
+
+      // 2020-01-15
+      statement.execute(
+          "insert into root.sg1.d1(timestamp, temperature) " + "values (1579053600000, 1)");
+      // 2020-03-16
+      statement.execute(
+          "insert into root.sg1.d1(timestamp, temperature) " + "values (1584324000000, 3)");
+      // 2020-06-17
+      statement.execute(
+          "insert into root.sg1.d1(timestamp, temperature) " + "values (1592359200000, 6)");
+      // 2020-09-18
+      statement.execute(
+          "insert into root.sg1.d1(timestamp, temperature) " + "values (1600394400000, 9)");
+      // 2020-12-19
+      statement.execute(
+          "insert into root.sg1.d1(timestamp, temperature) " + "values (1608343200000, 12)");
+
+      statement.execute("flush");
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+}
diff --git a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
index 1b6c5f7..1b95b3b 100644
--- a/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/qp/physical/PhysicalPlanTest.java
@@ -73,7 +73,6 @@ import org.apache.iotdb.tsfile.read.filter.TimeFilter;
 import org.apache.iotdb.tsfile.read.filter.ValueFilter;
 import org.apache.iotdb.tsfile.read.filter.factory.FilterFactory;
 
-import org.antlr.v4.runtime.misc.ParseCancellationException;
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
@@ -359,37 +358,40 @@ public class PhysicalPlanTest {
     }
   }
 
-  @Test
-  public void testGroupByFill2() {
-    String sqlStr =
-        "select last_value(s1) "
-            + " from root.vehicle.d1 "
-            + "group by([8,737), 3ms) fill(ALL[previousuntillast])";
-    try {
-      PhysicalPlan plan = processor.parseSQLToPhysicalPlan(sqlStr);
-      if (!plan.isQuery()) {
-        fail();
-      }
-      if (!(plan instanceof GroupByTimeFillPlan)) {
-        fail();
-      }
-      GroupByTimeFillPlan groupByFillPlan = (GroupByTimeFillPlan) plan;
-      assertEquals(3L, groupByFillPlan.getInterval());
-      assertEquals(3L, groupByFillPlan.getSlidingStep());
-      assertEquals(8L, groupByFillPlan.getStartTime());
-      assertEquals(737L, groupByFillPlan.getEndTime());
-      assertEquals(TSDataType.values().length, groupByFillPlan.getFillType().size());
-      for (TSDataType tsDataType : TSDataType.values()) {
-        assertTrue(groupByFillPlan.getFillType().containsKey(tsDataType));
-        assertTrue(groupByFillPlan.getFillType().get(tsDataType) instanceof PreviousFill);
-        PreviousFill previousFill = (PreviousFill) groupByFillPlan.getFillType().get(tsDataType);
-        assertTrue(previousFill.isUntilLast());
-      }
-    } catch (Exception e) {
-      e.printStackTrace();
-      fail();
-    }
-  }
+  // TODO: @CRZbulabula
+  //  support VECTOR in group by fill
+  //  @Test
+  //  public void testGroupByFill2() {
+  //    String sqlStr =
+  //        "select last_value(s1) "
+  //            + " from root.vehicle.d1 "
+  //            + "group by([8,737), 3ms) fill(ALL[previousuntillast])";
+  //    try {
+  //      PhysicalPlan plan = processor.parseSQLToPhysicalPlan(sqlStr);
+  //      if (!plan.isQuery()) {
+  //        fail();
+  //      }
+  //      if (!(plan instanceof GroupByTimeFillPlan)) {
+  //        fail();
+  //      }
+  //      GroupByTimeFillPlan groupByFillPlan = (GroupByTimeFillPlan) plan;
+  //      assertEquals(3L, groupByFillPlan.getInterval());
+  //      assertEquals(3L, groupByFillPlan.getSlidingStep());
+  //      assertEquals(8L, groupByFillPlan.getStartTime());
+  //      assertEquals(737L, groupByFillPlan.getEndTime());
+  //      assertEquals(TSDataType.values().length, groupByFillPlan.getFillType().size());
+  //      for (TSDataType tsDataType : TSDataType.values()) {
+  //        assertTrue(groupByFillPlan.getFillType().containsKey(tsDataType));
+  //        assertTrue(groupByFillPlan.getFillType().get(tsDataType) instanceof PreviousFill);
+  //        PreviousFill previousFill = (PreviousFill)
+  // groupByFillPlan.getFillType().get(tsDataType);
+  //        assertTrue(previousFill.isUntilLast());
+  //      }
+  //    } catch (Exception e) {
+  //      e.printStackTrace();
+  //      fail();
+  //    }
+  //  }
 
   @Test
   public void testGroupByFill3() {
@@ -434,12 +436,12 @@ public class PhysicalPlanTest {
     String sqlStr =
         "select last_value(d1.s1), last_value(d2.s1)"
             + " from root.vehicle "
-            + "group by([8,737), 3ms) fill(int32[linear])";
+            + "group by([8,737), 3ms) fill(boolean[linear])";
     try {
       processor.parseSQLToPhysicalPlan(sqlStr);
       fail();
     } catch (SQLParserException e) {
-      assertEquals("group by fill doesn't support linear fill", e.getMessage());
+      assertEquals("type BOOLEAN cannot use linear fill function", e.getMessage());
     } catch (Exception e) {
       e.printStackTrace();
       fail();
@@ -451,12 +453,12 @@ public class PhysicalPlanTest {
     String sqlStr =
         "select last_value(d1.s1), count(d2.s1)"
             + " from root.vehicle "
-            + "group by([8,737), 3ms) fill(int32[previous])";
+            + "group by([8,737), 3ms) fill(text[linear])";
     try {
       processor.parseSQLToPhysicalPlan(sqlStr);
       fail();
-    } catch (QueryProcessException e) {
-      assertEquals("Group By Fill only support last_value function", e.getMessage());
+    } catch (SQLParserException e) {
+      assertEquals("type TEXT cannot use linear fill function", e.getMessage());
     } catch (Exception e) {
       e.printStackTrace();
       fail();
@@ -466,23 +468,6 @@ public class PhysicalPlanTest {
   @Test
   public void testGroupByFill6() {
     String sqlStr =
-        "select count(s1)"
-            + "from root.vehicle.d1 "
-            + "group by([8,737), 3ms, 5ms) fill(int32[previous])";
-    try {
-      processor.parseSQLToPhysicalPlan(sqlStr);
-      fail();
-    } catch (ParseCancellationException e) {
-      assertTrue(e.getMessage().contains("mismatched input 'fill'"));
-    } catch (Exception e) {
-      e.printStackTrace();
-      fail();
-    }
-  }
-
-  @Test
-  public void testGroupByFill7() {
-    String sqlStr =
         "select last_value(d1.s1), last_value(d2.s1)"
             + " from root.vehicle "
             + "group by([8,737), 3ms) fill(int32[previousuntillast,10ms], int64[previous,10ms])";
diff --git a/server/src/test/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSetTest.java b/server/src/test/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSetTest.java
index 9e658e6..65d157b 100644
--- a/server/src/test/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSetTest.java
+++ b/server/src/test/java/org/apache/iotdb/db/query/dataset/groupby/GroupByFillDataSetTest.java
@@ -108,87 +108,93 @@ public class GroupByFillDataSetTest {
     }
   }
 
-  @Test
-  public void groupByWithValueFilterFillTest() throws Exception {
-    QueryPlan queryPlan =
-        (QueryPlan)
-            processor.parseSQLToPhysicalPlan(
-                "select last_value(s0) from root.vehicle.* where s1 > 1  group by ([0,20), 1ms) fill (int32[Previous]) order by time desc");
-    QueryDataSet dataSet =
-        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
-    for (int i = 19; i >= 7; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\t7", dataSet.next().toString());
-    }
-    assertTrue(dataSet.hasNext());
-    assertEquals("6\t6", dataSet.next().toString());
-    for (int i = 5; i >= 0; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\tnull", dataSet.next().toString());
-    }
-  }
-
-  @Test
-  public void groupByWithAndFilterFillTest() throws Exception {
-    QueryPlan queryPlan =
-        (QueryPlan)
-            processor.parseSQLToPhysicalPlan(
-                "select last_value(s0) from root.vehicle.* where s1 > 1 or s0 > 1  group by ([0,20), 1ms) fill (int32[Previous]) order by time desc");
-    QueryDataSet dataSet =
-        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
-    for (int i = 19; i >= 8; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\t8", dataSet.next().toString());
-    }
-    assertTrue(dataSet.hasNext());
-    assertEquals("7\t7", dataSet.next().toString());
-    assertTrue(dataSet.hasNext());
-    assertEquals("6\t6", dataSet.next().toString());
-    for (int i = 5; i >= 0; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\tnull", dataSet.next().toString());
-    }
-  }
+  // TODO: @CRZbulabula
+  //   GroupByFillWithValueFilterDataSet
+  //  @Test
+  //  public void groupByWithValueFilterFillTest() throws Exception {
+  //    QueryPlan queryPlan =
+  //        (QueryPlan)
+  //            processor.parseSQLToPhysicalPlan(
+  //                "select last_value(s0) from root.vehicle.* where s1 > 1  group by ([0,20), 1ms)
+  // fill (int32[Previous]) order by time desc");
+  //    QueryDataSet dataSet =
+  //        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
+  //    for (int i = 19; i >= 7; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\t7", dataSet.next().toString());
+  //    }
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("6\t6", dataSet.next().toString());
+  //    for (int i = 5; i >= 0; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\tnull", dataSet.next().toString());
+  //    }
+  //  }
 
-  @Test
-  public void groupByWithFirstNullTest() throws Exception {
-    QueryPlan queryPlan =
-        (QueryPlan)
-            processor.parseSQLToPhysicalPlan(
-                "select last_value(s0) from root.vehicle.* where s1 > 1 or s0 > 1  group by ([5,20), 1ms) fill (int32[Previous]) order by time desc");
-    QueryDataSet dataSet =
-        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
-    for (int i = 19; i >= 8; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\t8", dataSet.next().toString());
-    }
-    assertTrue(dataSet.hasNext());
-    assertEquals("7\t7", dataSet.next().toString());
-    assertTrue(dataSet.hasNext());
-    assertEquals("6\t6", dataSet.next().toString());
-    assertTrue(dataSet.hasNext());
-    assertEquals("5\t1", dataSet.next().toString());
-  }
-
-  @Test
-  public void groupByWithCross() throws Exception {
-    QueryPlan queryPlan =
-        (QueryPlan)
-            processor.parseSQLToPhysicalPlan(
-                "select last_value(s0) from root.vehicle.* where s2 > 1 group by ([0,20), 1ms) fill (int32[Previous]) order by time desc");
-    QueryDataSet dataSet =
-        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
-    for (int i = 19; i >= 8; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\t7", dataSet.next().toString());
-    }
-    assertTrue(dataSet.hasNext());
-    assertEquals("7\t7", dataSet.next().toString());
-    assertTrue(dataSet.hasNext());
-    assertEquals("6\t6", dataSet.next().toString());
-    for (int i = 5; i >= 0; i--) {
-      assertTrue(dataSet.hasNext());
-      assertEquals(i + "\tnull", dataSet.next().toString());
-    }
-  }
+  //  @Test
+  //  public void groupByWithAndFilterFillTest() throws Exception {
+  //    QueryPlan queryPlan =
+  //        (QueryPlan)
+  //            processor.parseSQLToPhysicalPlan(
+  //                "select last_value(s0) from root.vehicle.* where s1 > 1 or s0 > 1  group by
+  // ([0,20), 1ms) fill (int32[Previous]) order by time desc");
+  //    QueryDataSet dataSet =
+  //        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
+  //    for (int i = 19; i >= 8; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\t8", dataSet.next().toString());
+  //    }
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("7\t7", dataSet.next().toString());
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("6\t6", dataSet.next().toString());
+  //    for (int i = 5; i >= 0; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\tnull", dataSet.next().toString());
+  //    }
+  //  }
+  //
+  //  @Test
+  //  public void groupByWithFirstNullTest() throws Exception {
+  //    QueryPlan queryPlan =
+  //        (QueryPlan)
+  //            processor.parseSQLToPhysicalPlan(
+  //                "select last_value(s0) from root.vehicle.* where s1 > 1 or s0 > 1  group by
+  // ([5,20), 1ms) fill (int32[Previous]) order by time desc");
+  //    QueryDataSet dataSet =
+  //        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
+  //    for (int i = 19; i >= 8; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\t8", dataSet.next().toString());
+  //    }
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("7\t7", dataSet.next().toString());
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("6\t6", dataSet.next().toString());
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("5\t1", dataSet.next().toString());
+  //  }
+  //
+  //  @Test
+  //  public void groupByWithCross() throws Exception {
+  //    QueryPlan queryPlan =
+  //        (QueryPlan)
+  //            processor.parseSQLToPhysicalPlan(
+  //                "select last_value(s0) from root.vehicle.* where s2 > 1 group by ([0,20), 1ms)
+  // fill (int32[Previous]) order by time desc");
+  //    QueryDataSet dataSet =
+  //        queryExecutor.processQuery(queryPlan, EnvironmentUtils.TEST_QUERY_CONTEXT);
+  //    for (int i = 19; i >= 8; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\t7", dataSet.next().toString());
+  //    }
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("7\t7", dataSet.next().toString());
+  //    assertTrue(dataSet.hasNext());
+  //    assertEquals("6\t6", dataSet.next().toString());
+  //    for (int i = 5; i >= 0; i--) {
+  //      assertTrue(dataSet.hasNext());
+  //      assertEquals(i + "\tnull", dataSet.next().toString());
+  //    }
+  //  }
 }
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/RowRecord.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/RowRecord.java
index df0c051..748e8ab 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/RowRecord.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/common/RowRecord.java
@@ -25,7 +25,7 @@ import java.util.List;
 
 public class RowRecord {
 
-  private final long timestamp;
+  private long timestamp;
   private final List<Field> fields;
   /** if any column is null, this field should be set to true; otherwise false */
   private boolean hasNullField = false;
@@ -79,6 +79,10 @@ public class RowRecord {
     return sb.toString();
   }
 
+  public void setTimestamp(long timestamp) {
+    this.timestamp = timestamp;
+  }
+
   public long getTimestamp() {
     return timestamp;
   }