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

haonan pushed a commit to branch feature/continue_query
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/feature/continue_query by this 
push:
     new 9bfb98e  add cq
     new 1a2b4f9  Merge pull request #2988 from mzp0514/cq
9bfb98e is described below

commit 9bfb98e2f80360b613530872bf465eb6da2f878e
Author: mzp0514 <[email protected]>
AuthorDate: Tue Mar 30 21:00:23 2021 +0800

    add cq
---
 .../antlr4/org/apache/iotdb/db/qp/sql/SqlBase.g4   |  57 +++
 .../iotdb/cluster/log/applier/BaseApplier.java     |   6 +-
 .../iotdb/cluster/log/applier/MetaLogApplier.java  |   4 +-
 .../main/java/org/apache/iotdb/SessionExample.java |  21 ++
 .../org/apache/iotdb/db/auth/AuthorityChecker.java |   4 +
 .../apache/iotdb/db/auth/entity/PrivilegeType.java |   2 +
 .../org/apache/iotdb/db/conf/IoTDBConstant.java    |   6 +
 .../org/apache/iotdb/db/cq/ContinuousQuery.java    | 292 +++++++++++++++
 .../apache/iotdb/db/cq/ContinuousQueryService.java | 191 ++++++++++
 .../ContinuousQueryAlreadyExistException.java}     |  26 +-
 .../ContinuousQueryNotExistException.java}         |  26 +-
 .../apache/iotdb/db/metadata/MLogTxtWriter.java    |  25 ++
 .../org/apache/iotdb/db/metadata/MManager.java     |  39 +-
 .../iotdb/db/metadata/MetadataOperationType.java   |   2 +
 .../iotdb/db/metadata/logfile/MLogWriter.java      |  12 +
 .../org/apache/iotdb/db/mqtt/PublishHandler.java   |   4 +-
 .../main/java/org/apache/iotdb/db/qp/Planner.java  |  19 +
 .../apache/iotdb/db/qp/constant/SQLConstant.java   |   8 +
 .../apache/iotdb/db/qp/executor/IPlanExecutor.java |   3 +-
 .../apache/iotdb/db/qp/executor/PlanExecutor.java  |  62 +++-
 .../org/apache/iotdb/db/qp/logical/Operator.java   |   5 +-
 .../iotdb/db/qp/logical/crud/SelectOperator.java   |  31 ++
 .../logical/sys/CreateContinuousQueryOperator.java |  87 +++++
 .../logical/sys/DropContinuousQueryOperator.java}  |  30 +-
 .../sys/ShowContinuousQueriesOperator.java}        |  20 +-
 .../apache/iotdb/db/qp/physical/PhysicalPlan.java  |  14 +-
 .../qp/physical/sys/CreateContinuousQueryPlan.java | 132 +++++++
 .../qp/physical/sys/DropContinuousQueryPlan.java   |  63 ++++
 .../physical/sys/ShowContinuousQueriesPlan.java}   |  19 +-
 .../apache/iotdb/db/qp/physical/sys/ShowPlan.java  |   3 +-
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    | 193 +++++++++-
 .../iotdb/db/qp/strategy/PhysicalGenerator.java    |  20 ++
 .../query/dataset/ShowContinuousQueriesResult.java |  84 +++++
 .../query/dataset/groupby/GroupByTimeDataSet.java  |  11 +-
 .../db/query/udf/core/context/UDFContext.java      |  28 ++
 .../java/org/apache/iotdb/db/service/IoTDB.java    |   4 +
 .../org/apache/iotdb/db/service/ServiceType.java   |   3 +-
 .../org/apache/iotdb/db/service/TSServiceImpl.java |   3 +-
 .../org/apache/iotdb/db/tools/mlog/MLogParser.java |  15 +-
 .../integration/IoTDBCreateContinuousQueryIT.java  | 400 +++++++++++++++++++++
 .../iotdb/db/qp/physical/PhysicalPlanTest.java     | 124 +++++++
 .../java/org/apache/iotdb/rpc/TSStatusCode.java    |   2 +
 42 files changed, 2003 insertions(+), 97 deletions(-)

diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlBase.g4 
b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlBase.g4
index 0a3dbe3..9b43a9b 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlBase.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlBase.g4
@@ -107,6 +107,33 @@ statement
     fromClause
     whereClause?
     specialClause? #selectStatement
+    | CREATE CONTINUOUS QUERY continuousQueryName=ID
+      resampleClause?
+      BEGIN
+      cqSelectIntoClause
+      END  # createContinuousQueryStatement
+    | DROP CONTINUOUS QUERY continuousQueryName=ID# 
dropContinuousQueryStatement
+    | SHOW CONTINUOUS QUERIES # showContinuousQueriesStatement
+    ;
+
+
+resampleClause
+    : RESAMPLE (EVERY DURATION)? (FOR DURATION)?;
+
+cqSelectIntoClause
+    : SELECT selectElements
+    INTO (fullPath | suffixPath)
+    fromClause
+    whereClause?
+    cqGroupByTimeClause
+    ;
+
+
+cqGroupByTimeClause
+    : GROUP BY TIME LR_BRACKET
+      DURATION
+      RR_BRACKET
+      (COMMA LEVEL OPERATOR_EQ INT)?
     ;
 
 selectElements
@@ -646,6 +673,11 @@ nodeNameWithoutStar
     | PARTITION
     | DESC
     | ASC
+    | CONTINUOUS
+    | BEGIN
+    | END
+    | RESAMPLE
+    | EVERY
     ;
 
 dataType
@@ -1290,6 +1322,31 @@ EXPLAIN
     : E X P L A I N
     ;
 
+
+CONTINUOUS
+    : C O N T I N U O U S
+    ;
+
+QUERIES
+    : Q U E R I E S
+    ;
+
+BEGIN
+    : B E G I N
+    ;
+
+END
+    : E N D
+    ;
+
+RESAMPLE
+    : R E S A M P L E
+    ;
+
+EVERY
+    : E V E R Y
+    ;
+
 //============================
 // End of the keywords list
 //============================
diff --git 
a/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/BaseApplier.java 
b/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/BaseApplier.java
index 460ba6e..c25d27e 100644
--- 
a/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/BaseApplier.java
+++ 
b/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/BaseApplier.java
@@ -65,7 +65,7 @@ abstract class BaseApplier implements LogApplier {
    * @throws StorageEngineException
    */
   void applyPhysicalPlan(PhysicalPlan plan, DataGroupMember dataGroupMember)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, MetadataException, StorageEngineException {
     if (plan instanceof InsertPlan) {
       processPlanWithTolerance((InsertPlan) plan, dataGroupMember);
     } else if (plan != null && !plan.isQuery()) {
@@ -88,7 +88,7 @@ abstract class BaseApplier implements LogApplier {
   }
 
   private void executeAfterSync(PhysicalPlan plan)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, MetadataException, StorageEngineException {
     try {
       metaGroupMember.syncLeaderWithConsistencyCheck(true);
     } catch (CheckConsistencyException ce) {
@@ -106,7 +106,7 @@ abstract class BaseApplier implements LogApplier {
    * @throws StorageEngineException
    */
   private void processPlanWithTolerance(InsertPlan plan, DataGroupMember 
dataGroupMember)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, MetadataException, StorageEngineException {
     try {
       getQueryExecutor().processNonQuery(plan);
     } catch (QueryProcessException | StorageGroupNotSetException | 
StorageEngineException e) {
diff --git 
a/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/MetaLogApplier.java
 
b/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/MetaLogApplier.java
index 0556ed7..6b907d5 100644
--- 
a/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/MetaLogApplier.java
+++ 
b/cluster/src/main/java/org/apache/iotdb/cluster/log/applier/MetaLogApplier.java
@@ -26,7 +26,7 @@ import org.apache.iotdb.cluster.log.logtypes.RemoveNodeLog;
 import org.apache.iotdb.cluster.rpc.thrift.Node;
 import org.apache.iotdb.cluster.server.member.MetaGroupMember;
 import org.apache.iotdb.db.exception.StorageEngineException;
-import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 
 import org.slf4j.Logger;
@@ -59,7 +59,7 @@ public class MetaLogApplier extends BaseApplier {
       } else {
         logger.error("Unsupported log: {} {}", log.getClass().getName(), log);
       }
-    } catch (StorageEngineException | StorageGroupNotSetException | 
QueryProcessException e) {
+    } catch (StorageEngineException | MetadataException | 
QueryProcessException e) {
       logger.debug("Exception occurred when executing {}", log, e);
       log.setException(e);
     } finally {
diff --git a/example/session/src/main/java/org/apache/iotdb/SessionExample.java 
b/example/session/src/main/java/org/apache/iotdb/SessionExample.java
index 386b4e4..1116d47 100644
--- a/example/session/src/main/java/org/apache/iotdb/SessionExample.java
+++ b/example/session/src/main/java/org/apache/iotdb/SessionExample.java
@@ -71,6 +71,7 @@ public class SessionExample {
     insertTablet();
     insertTablets();
     insertRecords();
+    createAndDropContinuousQueries();
     nonQuery();
     query();
     queryWithTimeout();
@@ -93,6 +94,26 @@ public class SessionExample {
     session.close();
   }
 
+  private static void createAndDropContinuousQueries()
+      throws StatementExecutionException, IoTDBConnectionException {
+    session.executeNonQueryStatement(
+        "CREATE CONTINUOUS QUERY cq1 "
+            + "BEGIN SELECT max_value(s1) INTO temperature_max FROM root.sg1.* 
"
+            + "GROUP BY time(10s) END");
+    session.executeNonQueryStatement(
+        "CREATE CONTINUOUS QUERY cq2 "
+            + "BEGIN SELECT count(s2) INTO temperature_cnt FROM root.sg1.* "
+            + "WHERE s2 > 80 GROUP BY time(10s), level=1 END");
+    session.executeNonQueryStatement(
+        "CREATE CONTINUOUS QUERY cq3 "
+            + "RESAMPLE EVERY 20s FOR 20s "
+            + "BEGIN SELECT avg(s3) INTO temperature_avg FROM root.sg1.* "
+            + "GROUP BY time(10s), level=1 END");
+    session.executeNonQueryStatement("DROP CONTINUOUS QUERY cq1");
+    session.executeNonQueryStatement("DROP CONTINUOUS QUERY cq2");
+    session.executeNonQueryStatement("DROP CONTINUOUS QUERY cq3");
+  }
+
   private static void createTimeseries()
       throws IoTDBConnectionException, StatementExecutionException {
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java 
b/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
index fc5c680..b0dab97 100644
--- a/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
+++ b/server/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
@@ -157,6 +157,10 @@ public class AuthorityChecker {
         return PrivilegeType.START_TRIGGER.ordinal();
       case STOP_TRIGGER:
         return PrivilegeType.STOP_TRIGGER.ordinal();
+      case CREATE_CONTINUOUS_QUERY:
+        return PrivilegeType.CREATE_CONTINUOUS_QUERY.ordinal();
+      case DROP_CONTINUOUS_QUERY:
+        return PrivilegeType.DROP_CONTINUOUS_QUERY.ordinal();
       case AUTHOR:
       case METADATA:
       case BASIC_FUNC:
diff --git 
a/server/src/main/java/org/apache/iotdb/db/auth/entity/PrivilegeType.java 
b/server/src/main/java/org/apache/iotdb/db/auth/entity/PrivilegeType.java
index efde036..01dea3e 100644
--- a/server/src/main/java/org/apache/iotdb/db/auth/entity/PrivilegeType.java
+++ b/server/src/main/java/org/apache/iotdb/db/auth/entity/PrivilegeType.java
@@ -45,6 +45,8 @@ public enum PrivilegeType {
   DROP_TRIGGER,
   START_TRIGGER,
   STOP_TRIGGER,
+  CREATE_CONTINUOUS_QUERY,
+  DROP_CONTINUOUS_QUERY,
   ALL;
 
   /**
diff --git a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java 
b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
index dd9422d..802833f 100644
--- a/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/conf/IoTDBConstant.java
@@ -94,6 +94,12 @@ public class IoTDBConstant {
   public static final String COLUMN_FUNCTION_TYPE = "function type";
   public static final String COLUMN_FUNCTION_CLASS = "class name (UDF)";
 
+  public static final String COLUMN_CONTINUOUS_QUERY_NAME = "cq name";
+  public static final String COLUMN_CONTINUOUS_QUERY_EVERY_INTERVAL = "every 
interval";
+  public static final String COLUMN_CONTINUOUS_QUERY_FOR_INTERVAL = "for 
interval";
+  public static final String COLUMN_CONTINUOUS_QUERY_TARGET_PATH = "target 
path";
+  public static final String COLUMN_CONTINUOUS_QUERY_QUERY_SQL = "query sql";
+
   public static final String FUNCTION_TYPE_NATIVE = "native";
   public static final String FUNCTION_TYPE_BUILTIN_UDAF = "built-in UDAF";
   public static final String FUNCTION_TYPE_BUILTIN_UDTF = "built-in UDTF";
diff --git a/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQuery.java 
b/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQuery.java
new file mode 100644
index 0000000..9ea3519
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQuery.java
@@ -0,0 +1,292 @@
+/*
+ * 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.cq;
+
+import org.apache.iotdb.db.exception.StorageEngineException;
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.Planner;
+import org.apache.iotdb.db.qp.constant.SQLConstant;
+import org.apache.iotdb.db.qp.executor.PlanExecutor;
+import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+import org.apache.iotdb.db.qp.logical.crud.SelectOperator;
+import org.apache.iotdb.db.qp.physical.crud.GroupByTimePlan;
+import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.query.context.QueryContext;
+import org.apache.iotdb.db.query.control.QueryResourceManager;
+import 
org.apache.iotdb.tsfile.exception.filter.QueryFilterOptimizationException;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.read.common.Field;
+import org.apache.iotdb.tsfile.read.common.Path;
+import org.apache.iotdb.tsfile.read.common.RowRecord;
+import org.apache.iotdb.tsfile.read.query.dataset.QueryDataSet;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ContinuousQuery implements Runnable {
+
+  private final PlanExecutor planExecutor;
+  private final CreateContinuousQueryPlan plan;
+  private final Planner planner;
+
+  public ContinuousQuery(CreateContinuousQueryPlan plan) throws 
QueryProcessException {
+    this.plan = plan;
+    this.planExecutor = new PlanExecutor();
+    this.planner = new Planner();
+  }
+
+  private static TSDataType getAggrDataType(String aggrFuncName, TSDataType 
dataType) {
+    if (aggrFuncName == null) {
+      throw new IllegalArgumentException("AggregateFunction Name must not be 
null");
+    }
+
+    switch (aggrFuncName.toLowerCase()) {
+      case SQLConstant.MIN_TIME:
+      case SQLConstant.MAX_TIME:
+      case SQLConstant.COUNT:
+        return TSDataType.INT64;
+      case SQLConstant.MIN_VALUE:
+      case SQLConstant.LAST_VALUE:
+      case SQLConstant.FIRST_VALUE:
+      case SQLConstant.MAX_VALUE:
+        return dataType;
+      case SQLConstant.AVG:
+      case SQLConstant.SUM:
+        return TSDataType.DOUBLE;
+      default:
+        throw new IllegalArgumentException("Invalid Aggregation function: " + 
aggrFuncName);
+    }
+  }
+
+  @Override
+  public void run() {
+
+    try {
+
+      GroupByTimePlan queryPlan = getQueryPlan();
+
+      if (queryPlan.getDeduplicatedPaths().isEmpty()) {
+        return;
+      }
+
+      QueryDataSet result = doQuery(queryPlan);
+
+      if (result == null) {
+        return;
+      }
+
+      doInsert(result, queryPlan);
+
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+  }
+
+  private GroupByTimePlan getQueryPlan() throws QueryProcessException {
+    GroupByTimePlan queryPlan;
+
+    QueryOperator queryOperator = plan.getQueryOperator();
+
+    SelectOperator selectOperatorCopy = 
queryOperator.getSelectOperator().copy();
+    FilterOperator filterOperatorCopy = null;
+    if (queryOperator.getFilterOperator() != null) {
+      filterOperatorCopy = queryOperator.getFilterOperator().copy();
+    }
+
+    queryPlan = (GroupByTimePlan) 
planner.queryOperatorToPhysicalPlan(queryOperator, 1024);
+
+    queryOperator.setSelectOperator(selectOperatorCopy);
+    queryOperator.setFilterOperator(filterOperatorCopy);
+
+    long timestamp = System.currentTimeMillis();
+    queryPlan.setStartTime(timestamp - plan.getForInterval());
+    queryPlan.setEndTime(timestamp);
+
+    return queryPlan;
+  }
+
+  private QueryDataSet doQuery(GroupByTimePlan queryPlan)
+      throws StorageEngineException, QueryFilterOptimizationException, 
MetadataException,
+          IOException, InterruptedException, QueryProcessException {
+    long queryId =
+        QueryResourceManager.getInstance()
+            .assignQueryId(true, 1024, 
queryPlan.getDeduplicatedPaths().size());
+
+    QueryDataSet result;
+    result = planExecutor.processQuery(queryPlan, new QueryContext(queryId));
+    QueryResourceManager.getInstance().endQuery(queryId);
+    return result;
+  }
+
+  private void doInsert(QueryDataSet result, GroupByTimePlan queryPlan)
+      throws QueryProcessException, IOException, IllegalPathException {
+
+    int columnSize = result.getDataTypes().size();
+    TSDataType dataType =
+        getAggrDataType(queryPlan.getAggregations().get(0), 
queryPlan.getDataTypes().get(0));
+
+    InsertTabletPlan[] insertTabletPlans = getInsertTabletPlans(columnSize, 
result, dataType);
+
+    int fetchSize =
+        (int)
+            Math.min(
+                10, Math.ceil((float) plan.getForInterval() / 
plan.getQueryOperator().getUnit()));
+
+    Object[][] columns = getColumns(columnSize, fetchSize, dataType);
+    long[][] timestamps = new long[columnSize][fetchSize];
+    int[] rowNums = new int[columnSize];
+
+    boolean hasNext = true;
+
+    while (hasNext) {
+      int rowNum = 0;
+      Arrays.fill(rowNums, 0);
+
+      while (++rowNum <= fetchSize) {
+        if (!result.hasNextWithoutConstraint()) {
+          hasNext = false;
+          break;
+        }
+        RowRecord record = result.nextWithoutConstraint();
+        fillColumns(columns, dataType, record, rowNums, timestamps);
+      }
+
+      for (int i = 0; i < columnSize; i++) {
+        if (rowNums[i] > 0) {
+          insertTabletPlans[i].setTimes(timestamps[i]);
+          insertTabletPlans[i].setColumns(columns[i]);
+          insertTabletPlans[i].setRowCount(rowNums[i]);
+          planExecutor.insertTablet(insertTabletPlans[i]);
+        }
+      }
+    }
+  }
+
+  private InsertTabletPlan[] getInsertTabletPlans(
+      int columnSize, QueryDataSet result, TSDataType dataType) throws 
IllegalPathException {
+    List<PartialPath> targetPaths = getTargetPaths(result.getPaths());
+    InsertTabletPlan[] insertTabletPlans = new InsertTabletPlan[columnSize];
+    String[] measurements = new String[] {targetPaths.get(0).getMeasurement()};
+    List<Integer> dataTypes = Collections.singletonList(dataType.ordinal());
+
+    for (int i = 0; i < columnSize; i++) {
+      insertTabletPlans[i] =
+          new InsertTabletPlan(
+              new PartialPath(targetPaths.get(i).getDevice()), measurements, 
dataTypes);
+    }
+
+    return insertTabletPlans;
+  }
+
+  private Object[][] getColumns(int columnSize, int fetchSize, TSDataType 
dataType) {
+    Object[][] columns = new Object[columnSize][1];
+    for (int i = 0; i < columnSize; i++) {
+      switch (dataType) {
+        case DOUBLE:
+          columns[i][0] = new double[fetchSize];
+          break;
+        case INT64:
+          columns[i][0] = new long[fetchSize];
+          break;
+        case INT32:
+          columns[i][0] = new int[fetchSize];
+          break;
+        case FLOAT:
+          columns[i][0] = new float[fetchSize];
+          break;
+        default:
+          break;
+      }
+    }
+    return columns;
+  }
+
+  private void fillColumns(
+      Object[][] columns,
+      TSDataType dataType,
+      RowRecord record,
+      int[] rowNums,
+      long[][] timestamps) {
+    List<Field> fields = record.getFields();
+    long ts = record.getTimestamp();
+
+    for (int i = 0; i < columns.length; i++) {
+      Field field = fields.get(i);
+      if (field != null) {
+        timestamps[i][rowNums[i]] = ts;
+        switch (dataType) {
+          case DOUBLE:
+            ((double[]) columns[i][0])[rowNums[i]] = field.getDoubleV();
+            break;
+          case INT64:
+            ((long[]) columns[i][0])[rowNums[i]] = field.getLongV();
+            break;
+          case INT32:
+            ((int[]) columns[i][0])[rowNums[i]] = field.getIntV();
+            break;
+          case FLOAT:
+            ((float[]) columns[i][0])[rowNums[i]] = field.getFloatV();
+            break;
+          default:
+        }
+
+        rowNums[i]++;
+      }
+    }
+  }
+
+  private List<PartialPath> getTargetPaths(List<Path> rawPaths) throws 
IllegalPathException {
+    List<PartialPath> targetPaths = new ArrayList<>(rawPaths.size());
+    for (Path rawPath : rawPaths) {
+      targetPaths.add(new PartialPath(fillTemplate((PartialPath) rawPath)));
+    }
+    return targetPaths;
+  }
+
+  private String fillTemplate(PartialPath rawPath) {
+    String[] nodes = rawPath.getNodes();
+    int indexOfLeftBracket = nodes[0].indexOf("(");
+    if (indexOfLeftBracket != -1) {
+      nodes[0] = nodes[0].substring(indexOfLeftBracket + 1);
+    }
+    int indexOfRightBracket = nodes[nodes.length - 1].indexOf(")");
+    if (indexOfRightBracket != -1) {
+      nodes[nodes.length - 1] = nodes[nodes.length - 1].substring(0, 
indexOfRightBracket);
+    }
+    StringBuffer sb = new StringBuffer();
+    Matcher m = 
Pattern.compile("\\$\\{\\w+}").matcher(this.plan.getTargetPath().getFullPath());
+    while (m.find()) {
+      String param = m.group();
+      String value = nodes[Integer.parseInt(param.substring(2, param.length() 
- 1).trim())];
+      m.appendReplacement(sb, value == null ? "" : value);
+    }
+    m.appendTail(sb);
+    return sb.toString();
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQueryService.java 
b/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQueryService.java
new file mode 100644
index 0000000..0f4ec15
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/cq/ContinuousQueryService.java
@@ -0,0 +1,191 @@
+/*
+ * 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.cq;
+
+import org.apache.iotdb.db.concurrent.IoTDBThreadPoolFactory;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import 
org.apache.iotdb.db.exception.metadata.ContinuousQueryAlreadyExistException;
+import org.apache.iotdb.db.exception.metadata.ContinuousQueryNotExistException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
+import org.apache.iotdb.db.query.dataset.ShowContinuousQueriesResult;
+import org.apache.iotdb.db.service.IService;
+import org.apache.iotdb.db.service.IoTDB;
+import org.apache.iotdb.db.service.ServiceType;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.ReentrantLock;
+
+public class ContinuousQueryService implements IService {
+
+  private static final Logger logger = 
LoggerFactory.getLogger(ContinuousQueryService.class);
+
+  private final HashMap<String, ScheduledFuture<?>> continuousQueriesFutures = 
new HashMap<>();
+
+  private final HashMap<String, CreateContinuousQueryPlan> 
continuousQueryPlans = new HashMap<>();
+
+  private final ScheduledExecutorService pool =
+      IoTDBThreadPoolFactory.newScheduledThreadPool(10, "Continuous Query 
Service");
+
+  private final ReentrantLock registrationLock = new ReentrantLock();
+
+  private static final ContinuousQueryService INSTANCE = new 
ContinuousQueryService();
+
+  public static ContinuousQueryService getInstance() {
+    return INSTANCE;
+  }
+
+  public void acquireRegistrationLock() {
+    registrationLock.lock();
+  }
+
+  public void releaseRegistrationLock() {
+    registrationLock.unlock();
+  }
+
+  @Override
+  public ServiceType getID() {
+    return ServiceType.CONTINUOUS_QUERY_SERVICE;
+  }
+
+  @Override
+  public void start() {
+    logger.info("Continuous query service started.");
+  }
+
+  @Override
+  public void stop() {
+    pool.shutdownNow();
+  }
+
+  @Override
+  public void waitAndStop(long milliseconds) {
+    for (ScheduledFuture<?> future : continuousQueriesFutures.values()) {
+      future.cancel(false);
+    }
+    logger.info("Waiting for task pool to shut down");
+    try {
+      Thread.sleep(milliseconds);
+    } catch (InterruptedException e) {
+      logger.info("Thread interrupted");
+      Thread.currentThread().interrupt();
+    }
+    pool.shutdownNow();
+  }
+
+  public boolean register(CreateContinuousQueryPlan plan, boolean writeLog)
+      throws ContinuousQueryAlreadyExistException {
+
+    if (continuousQueryPlans.containsKey(plan.getContinuousQueryName())) {
+      throw new 
ContinuousQueryAlreadyExistException(plan.getContinuousQueryName());
+    }
+
+    if (writeLog) {
+      try {
+        IoTDB.metaManager.createContinuousQuery(plan);
+      } catch (IOException e) {
+        e.printStackTrace();
+      }
+    }
+
+    doRegister(plan);
+    return true;
+  }
+
+  private void doRegister(CreateContinuousQueryPlan plan) {
+    acquireRegistrationLock();
+
+    try {
+
+      ContinuousQuery cq = new ContinuousQuery(plan);
+      ScheduledFuture<?> future =
+          pool.scheduleAtFixedRate(
+              cq, plan.getEveryInterval(), plan.getEveryInterval(), 
getTimeUnit());
+      continuousQueriesFutures.put(plan.getContinuousQueryName(), future);
+      continuousQueryPlans.put(plan.getContinuousQueryName(), plan);
+    } catch (QueryProcessException e) {
+      e.printStackTrace();
+    } finally {
+      releaseRegistrationLock();
+    }
+  }
+
+  private TimeUnit getTimeUnit() {
+    String timestampPrecision = 
IoTDBDescriptor.getInstance().getConfig().getTimestampPrecision();
+    if (timestampPrecision.equals("us")) {
+      return TimeUnit.MICROSECONDS;
+    } else if (timestampPrecision.equals("ns")) {
+      return TimeUnit.NANOSECONDS;
+    } else {
+      return TimeUnit.MILLISECONDS;
+    }
+  }
+
+  public boolean deregister(DropContinuousQueryPlan plan) throws 
ContinuousQueryNotExistException {
+    if (!continuousQueryPlans.containsKey(plan.getContinuousQueryName())) {
+      throw new 
ContinuousQueryNotExistException(plan.getContinuousQueryName());
+    }
+
+    try {
+
+      IoTDB.metaManager.dropContinuousQuery(plan);
+      doDeregister(plan);
+    } catch (Exception e) {
+
+      e.printStackTrace();
+    }
+
+    return true;
+  }
+
+  private void doDeregister(DropContinuousQueryPlan plan) {
+    String cqName = plan.getContinuousQueryName();
+    continuousQueriesFutures.get(cqName).cancel(false);
+    continuousQueriesFutures.remove(cqName);
+    continuousQueryPlans.remove(cqName);
+  }
+
+  public List<ShowContinuousQueriesResult> getContinuousQueryPlans() {
+
+    List<ShowContinuousQueriesResult> results = new 
ArrayList<>(continuousQueryPlans.size());
+
+    for (CreateContinuousQueryPlan plan : continuousQueryPlans.values()) {
+      results.add(
+          new ShowContinuousQueriesResult(
+              plan.getQuerySql(),
+              plan.getContinuousQueryName(),
+              plan.getTargetPath(),
+              plan.getEveryInterval(),
+              plan.getForInterval()));
+    }
+
+    return results;
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryAlreadyExistException.java
similarity index 57%
copy from 
server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
copy to 
server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryAlreadyExistException.java
index 466c0a2..3f07589 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryAlreadyExistException.java
@@ -16,21 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata;
 
-public class MetadataOperationType {
+package org.apache.iotdb.db.exception.metadata;
 
-  private MetadataOperationType() {
-    // allowed to do nothing
-  }
+import org.apache.iotdb.rpc.TSStatusCode;
+
+public class ContinuousQueryAlreadyExistException extends MetadataException {
 
-  public static final String CREATE_TIMESERIES = "0";
-  public static final String DELETE_TIMESERIES = "1";
-  public static final String SET_STORAGE_GROUP = "2";
-  public static final String SET_TTL = "10";
-  public static final String DELETE_STORAGE_GROUP = "11";
-  public static final String CREATE_INDEX = "31";
-  public static final String DROP_INDEX = "32";
-  public static final String CHANGE_OFFSET = "12";
-  public static final String CHANGE_ALIAS = "13";
+  private static final long serialVersionUID = -6713847897890531438L;
+
+  public ContinuousQueryAlreadyExistException(String continuousQueryName) {
+    super(
+        String.format("Continuous Query [%s] already exist", 
continuousQueryName),
+        TSStatusCode.PATH_ALREADY_EXIST_ERROR.getStatusCode());
+    this.isUserException = true;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryNotExistException.java
similarity index 57%
copy from 
server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
copy to 
server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryNotExistException.java
index 466c0a2..f99685c 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/exception/metadata/ContinuousQueryNotExistException.java
@@ -16,21 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata;
 
-public class MetadataOperationType {
+package org.apache.iotdb.db.exception.metadata;
 
-  private MetadataOperationType() {
-    // allowed to do nothing
-  }
+import org.apache.iotdb.rpc.TSStatusCode;
+
+public class ContinuousQueryNotExistException extends MetadataException {
 
-  public static final String CREATE_TIMESERIES = "0";
-  public static final String DELETE_TIMESERIES = "1";
-  public static final String SET_STORAGE_GROUP = "2";
-  public static final String SET_TTL = "10";
-  public static final String DELETE_STORAGE_GROUP = "11";
-  public static final String CREATE_INDEX = "31";
-  public static final String DROP_INDEX = "32";
-  public static final String CHANGE_OFFSET = "12";
-  public static final String CHANGE_ALIAS = "13";
+  private static final long serialVersionUID = -6713847897890531438L;
+
+  public ContinuousQueryNotExistException(String continuousQueryName) {
+    super(
+        String.format("Continuous Query [%s] does not exist", 
continuousQueryName),
+        TSStatusCode.CONTINUOUS_QUERY_NOT_EXIST.getStatusCode());
+    this.isUserException = true;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MLogTxtWriter.java 
b/server/src/main/java/org/apache/iotdb/db/metadata/MLogTxtWriter.java
index ebfa2c6..a14ffb5 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MLogTxtWriter.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MLogTxtWriter.java
@@ -19,6 +19,8 @@
 package org.apache.iotdb.db.metadata;
 
 import org.apache.iotdb.db.engine.fileSystem.SystemFileFactory;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
 import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.MNodePlan;
 import org.apache.iotdb.db.qp.physical.sys.MeasurementMNodePlan;
@@ -126,6 +128,29 @@ public class MLogTxtWriter implements AutoCloseable {
     channel.write(buff);
   }
 
+  public void createContinuousQuery(CreateContinuousQueryPlan plan) throws 
IOException {
+    String buf =
+        String.format(
+                "%s,%s,%s,%s",
+                MetadataOperationType.CREATE_CONTINUOUS_QUERY,
+                plan.getContinuousQueryName(),
+                plan.getQuerySql(),
+                plan.getTargetPath().getFullPath())
+            + LINE_SEPARATOR;
+    channel.write(ByteBuffer.wrap(buf.getBytes()));
+    lineNumber.incrementAndGet();
+  }
+
+  public void dropContinuousQuery(DropContinuousQueryPlan plan) throws 
IOException {
+
+    String buf =
+        String.format(
+                "%s,%s", MetadataOperationType.DROP_CONTINUOUS_QUERY, 
plan.getContinuousQueryName())
+            + LINE_SEPARATOR;
+    channel.write(ByteBuffer.wrap(buf.getBytes()));
+    lineNumber.incrementAndGet();
+  }
+
   public void setStorageGroup(String storageGroup) throws IOException {
     String outputStr =
         MetadataOperationType.SET_STORAGE_GROUP + "," + storageGroup + 
LINE_SEPARATOR;
diff --git a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java 
b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
index c6bcb78..3ed8241 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/MManager.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.db.metadata;
 
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.cq.ContinuousQueryService;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.fileSystem.SystemFileFactory;
 import org.apache.iotdb.db.engine.storagegroup.StorageGroupProcessor;
@@ -40,10 +41,14 @@ import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode;
 import org.apache.iotdb.db.monitor.MonitorConstants;
 import org.apache.iotdb.db.qp.constant.SQLConstant;
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
 import org.apache.iotdb.db.qp.physical.PhysicalPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
 import org.apache.iotdb.db.qp.physical.sys.ChangeAliasPlan;
 import org.apache.iotdb.db.qp.physical.sys.ChangeTagOffsetPlan;
 import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan;
@@ -53,6 +58,7 @@ import 
org.apache.iotdb.db.qp.physical.sys.SetStorageGroupPlan;
 import org.apache.iotdb.db.qp.physical.sys.SetTTLPlan;
 import org.apache.iotdb.db.qp.physical.sys.ShowDevicesPlan;
 import org.apache.iotdb.db.qp.physical.sys.ShowTimeSeriesPlan;
+import org.apache.iotdb.db.qp.strategy.LogicalGenerator;
 import org.apache.iotdb.db.query.context.QueryContext;
 import org.apache.iotdb.db.query.dataset.ShowDevicesResult;
 import org.apache.iotdb.db.query.dataset.ShowTimeSeriesResult;
@@ -78,6 +84,7 @@ import org.slf4j.LoggerFactory;
 import java.io.File;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.time.ZoneId;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -287,6 +294,7 @@ public class MManager {
 
   private int applyMlog(MLogReader mLogReader) {
     int idx = 0;
+    HashMap<String, CreateContinuousQueryPlan> recoveredCQs = new HashMap<>();
     while (mLogReader.hasNext()) {
       PhysicalPlan plan = null;
       try {
@@ -294,13 +302,34 @@ public class MManager {
         if (plan == null) {
           continue;
         }
-        operation(plan);
+        if (plan.getOperatorType() == 
Operator.OperatorType.CREATE_CONTINUOUS_QUERY) {
+          recoveredCQs.put(
+              ((CreateContinuousQueryPlan) plan).getContinuousQueryName(),
+              (CreateContinuousQueryPlan) plan);
+        } else if (plan.getOperatorType() == 
Operator.OperatorType.DROP_CONTINUOUS_QUERY) {
+          recoveredCQs.remove(((DropContinuousQueryPlan) 
plan).getContinuousQueryName());
+        } else {
+          operation(plan);
+        }
         idx++;
       } catch (Exception e) {
         logger.error(
             "Can not operate cmd {} for err:", plan == null ? "" : 
plan.getOperatorType(), e);
       }
     }
+
+    LogicalGenerator logicalGenerator = new LogicalGenerator();
+    for (Map.Entry<String, CreateContinuousQueryPlan> cq : 
recoveredCQs.entrySet()) {
+      CreateContinuousQueryPlan plan = cq.getValue();
+      QueryOperator queryOperator =
+          (QueryOperator) logicalGenerator.generate(plan.getQuerySql(), 
ZoneId.systemDefault());
+      plan.setQueryOperator(queryOperator);
+      try {
+        ContinuousQueryService.getInstance().register(plan, false);
+      } catch (MetadataException e) {
+        e.printStackTrace();
+      }
+    }
     return idx;
   }
 
@@ -371,6 +400,14 @@ public class MManager {
     }
   }
 
+  public void createContinuousQuery(CreateContinuousQueryPlan plan) throws 
IOException {
+    logWriter.createContinuousQuery(plan);
+  }
+
+  public void dropContinuousQuery(DropContinuousQueryPlan plan) throws 
IOException {
+    logWriter.dropContinuousQuery(plan);
+  }
+
   public void createTimeseries(CreateTimeSeriesPlan plan) throws 
MetadataException {
     createTimeseries(plan, -1);
   }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
index 466c0a2..fe25640 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
@@ -33,4 +33,6 @@ public class MetadataOperationType {
   public static final String DROP_INDEX = "32";
   public static final String CHANGE_OFFSET = "12";
   public static final String CHANGE_ALIAS = "13";
+  public static final String CREATE_CONTINUOUS_QUERY = "14";
+  public static final String DROP_CONTINUOUS_QUERY = "15";
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java 
b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
index 8fda60d..cddda60 100644
--- a/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
+++ b/server/src/main/java/org/apache/iotdb/db/metadata/logfile/MLogWriter.java
@@ -28,6 +28,8 @@ import org.apache.iotdb.db.metadata.mnode.MNode;
 import org.apache.iotdb.db.metadata.mnode.MeasurementMNode;
 import org.apache.iotdb.db.metadata.mnode.StorageGroupMNode;
 import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
 import org.apache.iotdb.db.qp.physical.sys.ChangeAliasPlan;
 import org.apache.iotdb.db.qp.physical.sys.ChangeTagOffsetPlan;
 import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan;
@@ -125,6 +127,16 @@ public class MLogWriter implements AutoCloseable {
     putLog(deleteTimeSeriesPlan);
   }
 
+  public void createContinuousQuery(CreateContinuousQueryPlan 
createContinuousQueryPlan)
+      throws IOException {
+    putLog(createContinuousQueryPlan);
+  }
+
+  public void dropContinuousQuery(DropContinuousQueryPlan 
dropContinuousQueryPlan)
+      throws IOException {
+    putLog(dropContinuousQueryPlan);
+  }
+
   public void setStorageGroup(PartialPath storageGroup) throws IOException {
     SetStorageGroupPlan plan = new SetStorageGroupPlan(storageGroup);
     putLog(plan);
diff --git a/server/src/main/java/org/apache/iotdb/db/mqtt/PublishHandler.java 
b/server/src/main/java/org/apache/iotdb/db/mqtt/PublishHandler.java
index 1e4b213..27e1579 100644
--- a/server/src/main/java/org/apache/iotdb/db/mqtt/PublishHandler.java
+++ b/server/src/main/java/org/apache/iotdb/db/mqtt/PublishHandler.java
@@ -20,7 +20,7 @@ package org.apache.iotdb.db.mqtt;
 import org.apache.iotdb.db.conf.IoTDBConfig;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.exception.StorageEngineException;
-import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
+import org.apache.iotdb.db.exception.metadata.MetadataException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.qp.executor.IPlanExecutor;
@@ -118,7 +118,7 @@ public class PublishHandler extends 
AbstractInterceptHandler {
   }
 
   private boolean executeNonQuery(PhysicalPlan plan)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, MetadataException, StorageEngineException {
     if (IoTDBDescriptor.getInstance().getConfig().isReadOnly()) {
       throw new QueryProcessException(
           "Current system mode is read-only, does not support non-query 
operation");
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/Planner.java 
b/server/src/main/java/org/apache/iotdb/db/qp/Planner.java
index 355b7ef..df8c3ec 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/Planner.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/Planner.java
@@ -82,6 +82,22 @@ public class Planner {
     return physicalGenerator.transformToPhysicalPlan(operator, fetchSize);
   }
 
+  public PhysicalPlan queryOperatorToPhysicalPlan(SFWOperator queryOperator, 
int fetchSize)
+      throws QueryProcessException {
+    int maxDeduplicatedPathNum =
+        
QueryResourceManager.getInstance().getMaxDeduplicatedPathNum(fetchSize);
+    if (queryOperator.isLastQuery()) {
+      // Dataset of last query actually has only three columns, so we 
shouldn't limit the path num
+      // while constructing logical plan
+      // To avoid overflowing because logicalOptimize function may do 
maxDeduplicatedPathNum + 1, we
+      // set it to Integer.MAX_VALUE - 1
+      maxDeduplicatedPathNum = Integer.MAX_VALUE - 1;
+    }
+    queryOperator = optimizeSFWOperator(queryOperator, maxDeduplicatedPathNum);
+    PhysicalGenerator physicalGenerator = new PhysicalGenerator();
+    return physicalGenerator.transformToPhysicalPlan(queryOperator, fetchSize);
+  }
+
   /** convert raw data query to physical plan directly */
   public PhysicalPlan rawDataQueryReqToPhysicalPlan(
       TSRawDataQueryReq rawDataQueryReq, ZoneId zoneId)
@@ -181,6 +197,9 @@ public class Planner {
       case DROP_TRIGGER:
       case START_TRIGGER:
       case STOP_TRIGGER:
+      case CREATE_CONTINUOUS_QUERY:
+      case SHOW_CONTINUOUS_QUERIES:
+      case DROP_CONTINUOUS_QUERY:
         return operator;
       case QUERY:
       case DELETE:
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java 
b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
index 35dcdec..8ee51c7 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/constant/SQLConstant.java
@@ -178,6 +178,10 @@ public class SQLConstant {
   public static final int TOK_TRIGGER_STOP = 103;
   public static final int TOK_SHOW_TRIGGERS = 104;
 
+  public static final int TOK_CONTINUOUS_QUERY_CREATE = 105;
+  public static final int TOK_CONTINUOUS_QUERY_DROP = 106;
+  public static final int TOK_SHOW_CONTINUOUS_QUERIES = 107;
+
   public static final Map<Integer, String> tokenSymbol = new HashMap<>();
   public static final Map<Integer, String> tokenNames = new HashMap<>();
   public static final Map<Integer, Integer> reverseWords = new HashMap<>();
@@ -271,6 +275,10 @@ public class SQLConstant {
     tokenNames.put(TOK_TRIGGER_START, "TOK_TRIGGER_START");
     tokenNames.put(TOK_TRIGGER_STOP, "TOK_TRIGGER_STOP");
     tokenNames.put(TOK_SHOW_TRIGGERS, "TOK_SHOW_TRIGGERS");
+
+    tokenNames.put(TOK_CONTINUOUS_QUERY_CREATE, "TOK_CONTINUOUS_QUERY_CREATE");
+    tokenNames.put(TOK_CONTINUOUS_QUERY_DROP, "TOK_CONTINUOUS_QUERY_DROP");
+    tokenNames.put(TOK_SHOW_CONTINUOUS_QUERIES, "TOK_SHOW_CONTINUOUS_QUERIES");
   }
 
   static {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/executor/IPlanExecutor.java 
b/server/src/main/java/org/apache/iotdb/db/qp/executor/IPlanExecutor.java
index af24983..f302bdb 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/IPlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/IPlanExecutor.java
@@ -59,7 +59,8 @@ public interface IPlanExecutor {
    * @param plan Physical Non-Query Plan
    */
   boolean processNonQuery(PhysicalPlan plan)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException;
+      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException,
+          MetadataException;
 
   /**
    * execute update command and return whether the operator is successful.
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java 
b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
index 54a0bf7..221b5a8 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/executor/PlanExecutor.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.db.auth.entity.Role;
 import org.apache.iotdb.db.auth.entity.User;
 import org.apache.iotdb.db.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
+import org.apache.iotdb.db.cq.ContinuousQueryService;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.cache.ChunkCache;
 import org.apache.iotdb.db.engine.cache.TimeSeriesMetadataCache;
@@ -43,6 +44,8 @@ import 
org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.exception.metadata.MetadataException;
 import org.apache.iotdb.db.exception.metadata.PathNotExistException;
 import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
+import 
org.apache.iotdb.db.exception.metadata.ContinuousQueryAlreadyExistException;
+import org.apache.iotdb.db.exception.metadata.ContinuousQueryNotExistException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.metadata.mnode.MNode;
@@ -72,6 +75,9 @@ import org.apache.iotdb.db.qp.physical.crud.QueryIndexPlan;
 import org.apache.iotdb.db.qp.physical.crud.QueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.RawDataQueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.ShowContinuousQueriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AlterTimeSeriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AuthorPlan;
 import org.apache.iotdb.db.qp.physical.sys.CountPlan;
@@ -111,6 +117,7 @@ import org.apache.iotdb.db.query.dataset.ListDataSet;
 import org.apache.iotdb.db.query.dataset.ShowDevicesDataSet;
 import org.apache.iotdb.db.query.dataset.ShowTimeseriesDataSet;
 import org.apache.iotdb.db.query.dataset.SingleDataSet;
+import org.apache.iotdb.db.query.dataset.ShowContinuousQueriesResult;
 import org.apache.iotdb.db.query.executor.IQueryRouter;
 import org.apache.iotdb.db.query.executor.QueryRouter;
 import org.apache.iotdb.db.query.udf.service.UDFRegistrationInformation;
@@ -185,6 +192,11 @@ import static 
org.apache.iotdb.db.conf.IoTDBConstant.FUNCTION_TYPE_NATIVE;
 import static org.apache.iotdb.db.conf.IoTDBConstant.QUERY_ID;
 import static org.apache.iotdb.db.conf.IoTDBConstant.STATEMENT;
 import static 
org.apache.iotdb.tsfile.common.constant.TsFileConstant.TSFILE_SUFFIX;
+import static 
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_CONTINUOUS_QUERY_EVERY_INTERVAL;
+import static 
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_CONTINUOUS_QUERY_FOR_INTERVAL;
+import static 
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_CONTINUOUS_QUERY_NAME;
+import static 
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_CONTINUOUS_QUERY_QUERY_SQL;
+import static 
org.apache.iotdb.db.conf.IoTDBConstant.COLUMN_CONTINUOUS_QUERY_TARGET_PATH;
 
 @SuppressWarnings("java:S1135") // ignore todos
 public class PlanExecutor implements IPlanExecutor {
@@ -222,7 +234,7 @@ public class PlanExecutor implements IPlanExecutor {
 
   @Override
   public boolean processNonQuery(PhysicalPlan plan)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, StorageEngineException, MetadataException {
     switch (plan.getOperatorType()) {
       case DELETE:
         delete((DeletePlan) plan);
@@ -335,6 +347,10 @@ public class PlanExecutor implements IPlanExecutor {
           throw new QueryProcessException(e.getMessage());
         }
         return true;
+      case CREATE_CONTINUOUS_QUERY:
+        return operateCreateContinuousQuery((CreateContinuousQueryPlan) plan);
+      case DROP_CONTINUOUS_QUERY:
+        return operateDropContinuousQuery((DropContinuousQueryPlan) plan);
       default:
         throw new UnsupportedOperationException(
             String.format("operation %s is not supported", 
plan.getOperatorType()));
@@ -430,6 +446,16 @@ public class PlanExecutor implements IPlanExecutor {
     }
   }
 
+  private boolean operateCreateContinuousQuery(CreateContinuousQueryPlan plan)
+      throws ContinuousQueryAlreadyExistException {
+    return ContinuousQueryService.getInstance().register(plan, true);
+  }
+
+  private boolean operateDropContinuousQuery(DropContinuousQueryPlan plan)
+      throws ContinuousQueryNotExistException {
+    return ContinuousQueryService.getInstance().deregister(plan);
+  }
+
   public static void flushSpecifiedStorageGroups(FlushPlan plan)
       throws StorageGroupNotSetException {
     Map<PartialPath, List<Pair<Long, Boolean>>> storageGroupMap =
@@ -539,6 +565,8 @@ public class PlanExecutor implements IPlanExecutor {
         return processShowFunctions((ShowFunctionsPlan) showPlan);
       case TRIGGERS:
         return processShowTriggers((ShowTriggersPlan) showPlan);
+      case CONTINUOUS_QUERY:
+        return processShowContinuousQueries();
       default:
         throw new QueryProcessException(String.format("Unrecognized show plan 
%s", showPlan));
     }
@@ -859,6 +887,38 @@ public class PlanExecutor implements IPlanExecutor {
     }
   }
 
+  private QueryDataSet processShowContinuousQueries() {
+    ListDataSet listDataSet =
+        new ListDataSet(
+            Arrays.asList(
+                new PartialPath(COLUMN_CONTINUOUS_QUERY_NAME, false),
+                new PartialPath(COLUMN_CONTINUOUS_QUERY_EVERY_INTERVAL, false),
+                new PartialPath(COLUMN_CONTINUOUS_QUERY_FOR_INTERVAL, false),
+                new PartialPath(COLUMN_CONTINUOUS_QUERY_QUERY_SQL, false),
+                new PartialPath(COLUMN_CONTINUOUS_QUERY_TARGET_PATH, false)),
+            Arrays.asList(
+                TSDataType.TEXT,
+                TSDataType.INT64,
+                TSDataType.INT64,
+                TSDataType.TEXT,
+                TSDataType.TEXT));
+
+    List<ShowContinuousQueriesResult> continuousQueriesList =
+        ContinuousQueryService.getInstance().getContinuousQueryPlans();
+
+    for (ShowContinuousQueriesResult result : continuousQueriesList) {
+      RowRecord record = new RowRecord(0);
+      record.addField(Binary.valueOf(result.getContinuousQueryName()), 
TSDataType.TEXT);
+      record.addField(result.getEveryInterval(), TSDataType.INT64);
+      record.addField(result.getForInterval(), TSDataType.INT64);
+      record.addField(Binary.valueOf(result.getQuerySql()), TSDataType.TEXT);
+      record.addField(Binary.valueOf(result.getTargetPath().getFullPath()), 
TSDataType.TEXT);
+      listDataSet.putRecord(record);
+    }
+
+    return listDataSet;
+  }
+
   private void appendNativeFunctions(ListDataSet listDataSet, 
ShowFunctionsPlan showPlan) {
     if (showPlan.showTemporary()) {
       return;
diff --git a/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
index f2a8633..6a0514f 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/logical/Operator.java
@@ -161,6 +161,9 @@ public abstract class Operator {
     CREATE_TRIGGER,
     DROP_TRIGGER,
     START_TRIGGER,
-    STOP_TRIGGER
+    STOP_TRIGGER,
+    CREATE_CONTINUOUS_QUERY,
+    DROP_CONTINUOUS_QUERY,
+    SHOW_CONTINUOUS_QUERIES
   }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectOperator.java 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectOperator.java
index 5b34ea5..5790b80 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectOperator.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/crud/SelectOperator.java
@@ -18,6 +18,7 @@
  */
 package org.apache.iotdb.db.qp.logical.crud;
 
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.qp.logical.Operator;
 import org.apache.iotdb.db.query.udf.core.context.UDFContext;
@@ -113,4 +114,34 @@ public final class SelectOperator extends Operator {
   public void setUdfList(List<UDFContext> udfList) {
     this.udfList = udfList;
   }
+
+  public SelectOperator copy() {
+    SelectOperator ret = new SelectOperator(this.tokenIntType, this.zoneId);
+
+    try {
+      for (PartialPath path : this.suffixList) {
+        ret.suffixList.add(new PartialPath(path.getFullPath()));
+      }
+    } catch (IllegalPathException e) {
+      e.printStackTrace();
+    }
+
+    for (String aggr : this.aggregations) {
+      ret.aggregations.add(aggr);
+    }
+
+    for (UDFContext ctx : this.udfList) {
+      if (ctx != null) {
+        ret.udfList.add(ctx.copy());
+      } else {
+        ret.udfList.add(null);
+      }
+    }
+
+    ret.lastQuery = this.lastQuery;
+    ret.udfQuery = this.udfQuery;
+    ret.hasBuiltinAggregation = this.hasBuiltinAggregation;
+
+    return ret;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/CreateContinuousQueryOperator.java
 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/CreateContinuousQueryOperator.java
new file mode 100644
index 0000000..e1516d1
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/CreateContinuousQueryOperator.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.db.qp.logical.sys;
+
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.logical.RootOperator;
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+
+public class CreateContinuousQueryOperator extends RootOperator {
+
+  private String querySql;
+  private QueryOperator queryOperator;
+  private String continuousQueryName;
+  private PartialPath targetPath;
+  private long everyInterval;
+  private long forInterval;
+
+  public CreateContinuousQueryOperator(int tokenIntType) {
+    super(tokenIntType);
+    operatorType = OperatorType.CREATE_CONTINUOUS_QUERY;
+  }
+
+  public void setQuerySql(String querySql) {
+    this.querySql = querySql;
+  }
+
+  public String getQuerySql() {
+    return querySql;
+  }
+
+  public void setContinuousQueryName(String continuousQueryName) {
+    this.continuousQueryName = continuousQueryName;
+  }
+
+  public String getContinuousQueryName() {
+    return continuousQueryName;
+  }
+
+  public void setTargetPath(PartialPath targetPath) {
+    this.targetPath = targetPath;
+  }
+
+  public PartialPath getTargetPath() {
+    return targetPath;
+  }
+
+  public void setEveryInterval(long everyInterval) {
+    this.everyInterval = everyInterval;
+  }
+
+  public long getEveryInterval() {
+    return everyInterval;
+  }
+
+  public void setForInterval(long forInterval) {
+    this.forInterval = forInterval;
+  }
+
+  public long getForInterval() {
+    return forInterval;
+  }
+
+  public void setQueryOperator(QueryOperator queryOperator) {
+    this.queryOperator = queryOperator;
+  }
+
+  public QueryOperator getQueryOperator() {
+    return queryOperator;
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/DropContinuousQueryOperator.java
similarity index 57%
copy from 
server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
copy to 
server/src/main/java/org/apache/iotdb/db/qp/logical/sys/DropContinuousQueryOperator.java
index 466c0a2..34b79aa 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/DropContinuousQueryOperator.java
@@ -16,21 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata;
 
-public class MetadataOperationType {
+package org.apache.iotdb.db.qp.logical.sys;
 
-  private MetadataOperationType() {
-    // allowed to do nothing
+import org.apache.iotdb.db.qp.logical.RootOperator;
+
+public class DropContinuousQueryOperator extends RootOperator {
+
+  private String continuousQueryName;
+
+  public DropContinuousQueryOperator(int tokenIntType) {
+    super(tokenIntType);
+    operatorType = OperatorType.DROP_CONTINUOUS_QUERY;
   }
 
-  public static final String CREATE_TIMESERIES = "0";
-  public static final String DELETE_TIMESERIES = "1";
-  public static final String SET_STORAGE_GROUP = "2";
-  public static final String SET_TTL = "10";
-  public static final String DELETE_STORAGE_GROUP = "11";
-  public static final String CREATE_INDEX = "31";
-  public static final String DROP_INDEX = "32";
-  public static final String CHANGE_OFFSET = "12";
-  public static final String CHANGE_ALIAS = "13";
+  public void setContinuousQueryName(String continuousQueryName) {
+    this.continuousQueryName = continuousQueryName;
+  }
+
+  public String getContinuousQueryName() {
+    return continuousQueryName;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/ShowContinuousQueriesOperator.java
similarity index 57%
copy from 
server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
copy to 
server/src/main/java/org/apache/iotdb/db/qp/logical/sys/ShowContinuousQueriesOperator.java
index 466c0a2..c5669ab 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/logical/sys/ShowContinuousQueriesOperator.java
@@ -16,21 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata;
 
-public class MetadataOperationType {
+package org.apache.iotdb.db.qp.logical.sys;
 
-  private MetadataOperationType() {
-    // allowed to do nothing
-  }
+public class ShowContinuousQueriesOperator extends ShowOperator {
 
-  public static final String CREATE_TIMESERIES = "0";
-  public static final String DELETE_TIMESERIES = "1";
-  public static final String SET_STORAGE_GROUP = "2";
-  public static final String SET_TTL = "10";
-  public static final String DELETE_STORAGE_GROUP = "11";
-  public static final String CREATE_INDEX = "31";
-  public static final String DROP_INDEX = "32";
-  public static final String CHANGE_OFFSET = "12";
-  public static final String CHANGE_ALIAS = "13";
+  public ShowContinuousQueriesOperator(int tokenIntType) {
+    super(tokenIntType);
+    operatorType = OperatorType.SHOW_CONTINUOUS_QUERIES;
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/PhysicalPlan.java 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/PhysicalPlan.java
index 16719c4..c526d93 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/physical/PhysicalPlan.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/PhysicalPlan.java
@@ -28,6 +28,7 @@ import 
org.apache.iotdb.db.qp.physical.crud.InsertMultiTabletPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertRowPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertRowsPlan;
 import org.apache.iotdb.db.qp.physical.crud.InsertTabletPlan;
+import org.apache.iotdb.db.qp.physical.sys.*;
 import org.apache.iotdb.db.qp.physical.sys.AlterTimeSeriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AuthorPlan;
 import org.apache.iotdb.db.qp.physical.sys.ChangeAliasPlan;
@@ -368,6 +369,14 @@ public abstract class PhysicalPlan {
           plan = new InsertRowsPlan();
           plan.deserialize(buffer);
           break;
+        case CREATE_CONTINUOUS_QUERY:
+          plan = new CreateContinuousQueryPlan();
+          plan.deserialize(buffer);
+          break;
+        case DROP_CONTINUOUS_QUERY:
+          plan = new DropContinuousQueryPlan();
+          plan.deserialize(buffer);
+          break;
         default:
           throw new IOException("unrecognized log type " + type);
       }
@@ -412,7 +421,10 @@ public abstract class PhysicalPlan {
     BATCH_INSERT_ONE_DEVICE,
     MULTI_BATCH_INSERT,
     BATCH_INSERT_ROWS,
-    SHOW_DEVICES
+    SHOW_DEVICES,
+    CREATE_CONTINUOUS_QUERY,
+    DROP_CONTINUOUS_QUERY,
+    SHOW_CONTINUOUS_QUERIES
   }
 
   public long getIndex() {
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/CreateContinuousQueryPlan.java
 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/CreateContinuousQueryPlan.java
new file mode 100644
index 0000000..3917143
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/CreateContinuousQueryPlan.java
@@ -0,0 +1,132 @@
+/*
+ * 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.qp.physical.sys;
+
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.nio.ByteBuffer;
+import java.util.*;
+
+public class CreateContinuousQueryPlan extends PhysicalPlan implements 
java.io.Serializable {
+
+  private String querySql;
+  private String continuousQueryName;
+  private PartialPath targetPath;
+  private long everyInterval;
+  private long forInterval;
+  private transient QueryOperator queryOperator;
+
+  public CreateContinuousQueryPlan() {
+    super(false, Operator.OperatorType.CREATE_CONTINUOUS_QUERY);
+  }
+
+  public CreateContinuousQueryPlan(
+      String querySql,
+      String continuousQueryName,
+      PartialPath targetPath,
+      long everyInterval,
+      long forInterval,
+      QueryOperator queryOperator) {
+    super(false, Operator.OperatorType.CREATE_CONTINUOUS_QUERY);
+    this.querySql = querySql;
+    this.continuousQueryName = continuousQueryName;
+    this.targetPath = targetPath;
+    this.everyInterval = everyInterval;
+    this.forInterval = forInterval;
+    this.queryOperator = queryOperator;
+  }
+
+  public void setQuerySql(String querySql) {
+    this.querySql = querySql;
+  }
+
+  public String getQuerySql() {
+    return querySql;
+  }
+
+  public void setContinuousQueryName(String continuousQueryName) {
+    this.continuousQueryName = continuousQueryName;
+  }
+
+  public String getContinuousQueryName() {
+    return continuousQueryName;
+  }
+
+  public void setTargetPath(PartialPath targetPath) {
+    this.targetPath = targetPath;
+  }
+
+  public PartialPath getTargetPath() {
+    return targetPath;
+  }
+
+  public void setEveryInterval(long everyInterval) {
+    this.everyInterval = everyInterval;
+  }
+
+  public long getEveryInterval() {
+    return everyInterval;
+  }
+
+  public void setForInterval(long forInterval) {
+    this.forInterval = forInterval;
+  }
+
+  public long getForInterval() {
+    return forInterval;
+  }
+
+  public void setQueryOperator(QueryOperator queryOperator) {
+    this.queryOperator = queryOperator;
+  }
+
+  public QueryOperator getQueryOperator() {
+    return queryOperator;
+  }
+
+  @Override
+  public List<PartialPath> getPaths() {
+    return Collections.emptyList();
+  }
+
+  @Override
+  public void serialize(ByteBuffer buffer) {
+    buffer.put((byte) PhysicalPlanType.CREATE_CONTINUOUS_QUERY.ordinal());
+    ReadWriteIOUtils.write(continuousQueryName, buffer);
+    ReadWriteIOUtils.write(querySql, buffer);
+    ReadWriteIOUtils.write(targetPath.getFullPath(), buffer);
+    buffer.putLong(everyInterval);
+    buffer.putLong(forInterval);
+  }
+
+  @Override
+  public void deserialize(ByteBuffer buffer) throws IllegalPathException {
+    continuousQueryName = ReadWriteIOUtils.readString(buffer);
+    querySql = ReadWriteIOUtils.readString(buffer);
+    targetPath = new PartialPath(ReadWriteIOUtils.readString(buffer));
+    everyInterval = ReadWriteIOUtils.readLong(buffer);
+    forInterval = ReadWriteIOUtils.readLong(buffer);
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/DropContinuousQueryPlan.java
 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/DropContinuousQueryPlan.java
new file mode 100644
index 0000000..1faa458
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/DropContinuousQueryPlan.java
@@ -0,0 +1,63 @@
+/*
+ * 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.qp.physical.sys;
+
+import org.apache.iotdb.db.metadata.PartialPath;
+import org.apache.iotdb.db.qp.logical.Operator;
+import org.apache.iotdb.db.qp.physical.PhysicalPlan;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+public class DropContinuousQueryPlan extends PhysicalPlan {
+
+  private String continuousQueryName;
+
+  public DropContinuousQueryPlan() {
+    super(false, Operator.OperatorType.DROP_CONTINUOUS_QUERY);
+  }
+
+  public DropContinuousQueryPlan(String continuousQueryName) {
+    super(false, Operator.OperatorType.DROP_CONTINUOUS_QUERY);
+    this.continuousQueryName = continuousQueryName;
+  }
+
+  @Override
+  public List<PartialPath> getPaths() {
+    return new ArrayList<>();
+  }
+
+  public String getContinuousQueryName() {
+    return continuousQueryName;
+  }
+
+  @Override
+  public void serialize(ByteBuffer buffer) {
+    buffer.put((byte) PhysicalPlanType.DROP_CONTINUOUS_QUERY.ordinal());
+    ReadWriteIOUtils.write(continuousQueryName, buffer);
+  }
+
+  @Override
+  public void deserialize(ByteBuffer buffer) {
+    continuousQueryName = ReadWriteIOUtils.readString(buffer);
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowContinuousQueriesPlan.java
similarity index 57%
copy from 
server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
copy to 
server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowContinuousQueriesPlan.java
index 466c0a2..7a4b6ca 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/metadata/MetadataOperationType.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowContinuousQueriesPlan.java
@@ -16,21 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.iotdb.db.metadata;
 
-public class MetadataOperationType {
+package org.apache.iotdb.db.qp.physical.sys;
 
-  private MetadataOperationType() {
-    // allowed to do nothing
-  }
+public class ShowContinuousQueriesPlan extends ShowPlan {
 
-  public static final String CREATE_TIMESERIES = "0";
-  public static final String DELETE_TIMESERIES = "1";
-  public static final String SET_STORAGE_GROUP = "2";
-  public static final String SET_TTL = "10";
-  public static final String DELETE_STORAGE_GROUP = "11";
-  public static final String CREATE_INDEX = "31";
-  public static final String DROP_INDEX = "32";
-  public static final String CHANGE_OFFSET = "12";
-  public static final String CHANGE_ALIAS = "13";
+  public ShowContinuousQueriesPlan() {
+    super(ShowContentType.CONTINUOUS_QUERY);
+  }
 }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowPlan.java 
b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowPlan.java
index a77917a..687c9f0 100644
--- a/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowPlan.java
+++ b/server/src/main/java/org/apache/iotdb/db/qp/physical/sys/ShowPlan.java
@@ -117,6 +117,7 @@ public class ShowPlan extends PhysicalPlan {
     COUNT_DEVICES,
     COUNT_STORAGE_GROUP,
     QUERY_PROCESSLIST,
-    TRIGGERS
+    TRIGGERS,
+    CONTINUOUS_QUERY
   }
 }
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 cfd923b..1ca050e 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
@@ -35,7 +35,7 @@ import org.apache.iotdb.db.qp.logical.crud.InOperator;
 import org.apache.iotdb.db.qp.logical.crud.InsertOperator;
 import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
 import org.apache.iotdb.db.qp.logical.crud.SelectOperator;
-import org.apache.iotdb.db.qp.logical.sys.AlterTimeSeriesOperator;
+import org.apache.iotdb.db.qp.logical.sys.*;
 import org.apache.iotdb.db.qp.logical.sys.AlterTimeSeriesOperator.AlterType;
 import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
 import org.apache.iotdb.db.qp.logical.sys.AuthorOperator.AuthorType;
@@ -225,6 +225,8 @@ import 
org.apache.iotdb.tsfile.file.metadata.enums.TSEncoding;
 import org.apache.iotdb.tsfile.utils.Pair;
 import org.apache.iotdb.tsfile.utils.StringContainer;
 
+import org.antlr.v4.runtime.ParserRuleContext;
+import org.antlr.v4.runtime.misc.Interval;
 import org.antlr.v4.runtime.tree.TerminalNode;
 
 import java.io.File;
@@ -1035,6 +1037,193 @@ public class IoTDBSqlVisitor extends 
SqlBaseBaseVisitor<Operator> {
   }
 
   @Override
+  public Operator visitDropContinuousQueryStatement(
+      SqlBaseParser.DropContinuousQueryStatementContext ctx) {
+    DropContinuousQueryOperator operator =
+        new DropContinuousQueryOperator(SQLConstant.TOK_CONTINUOUS_QUERY_DROP);
+    operator.setContinuousQueryName(ctx.continuousQueryName.getText());
+    return operator;
+  }
+
+  @Override
+  public Operator visitShowContinuousQueriesStatement(
+      SqlBaseParser.ShowContinuousQueriesStatementContext ctx) {
+    return new 
ShowContinuousQueriesOperator(SQLConstant.TOK_SHOW_CONTINUOUS_QUERIES);
+  }
+
+  @Override
+  public Operator visitCreateContinuousQueryStatement(
+      SqlBaseParser.CreateContinuousQueryStatementContext ctx) {
+    CreateContinuousQueryOperator createContinuousQueryOperator =
+        new 
CreateContinuousQueryOperator(SQLConstant.TOK_CONTINUOUS_QUERY_CREATE);
+
+    createContinuousQueryOperator.setQuerySql(ctx.getText());
+
+    
createContinuousQueryOperator.setContinuousQueryName(ctx.continuousQueryName.getText());
+
+    if (ctx.resampleClause() != null) {
+      parseResampleClause(ctx.resampleClause(), createContinuousQueryOperator);
+    }
+
+    parseCqSelectIntoClause(ctx.cqSelectIntoClause(), 
createContinuousQueryOperator);
+
+    QueryOperator queryOperator = 
createContinuousQueryOperator.getQueryOperator();
+
+    StringBuilder sb = new StringBuilder();
+    sb.append("select ");
+    sb.append(ctx.cqSelectIntoClause().selectElements().getText());
+    sb.append(" from ");
+    sb.append(ctx.cqSelectIntoClause().fromClause().prefixPath(0).getText());
+    if (ctx.cqSelectIntoClause().whereClause() != null) {
+      sb.append(" ");
+      
sb.append(getFullText(ctx.cqSelectIntoClause().whereClause()).toLowerCase());
+    }
+    sb.append(" group by ([now() - ");
+    String groupByInterval = 
ctx.cqSelectIntoClause().cqGroupByTimeClause().DURATION().getText();
+    if (createContinuousQueryOperator.getForInterval() == 0) {
+      sb.append(groupByInterval);
+    } else {
+      List<TerminalNode> durations = ctx.resampleClause().DURATION();
+      sb.append(durations.get(durations.size() - 1).getText());
+    }
+    sb.append(", now()), ");
+    sb.append(groupByInterval);
+    sb.append(")");
+    if (queryOperator.isGroupByLevel()) {
+      sb.append(", level = ");
+      sb.append(queryOperator.getLevel());
+    }
+    createContinuousQueryOperator.setQuerySql(sb.toString());
+
+    if (createContinuousQueryOperator.getEveryInterval() == 0) {
+      if (createContinuousQueryOperator.getForInterval() == 0) {
+        
createContinuousQueryOperator.setEveryInterval(queryOperator.getUnit());
+        createContinuousQueryOperator.setForInterval(queryOperator.getUnit());
+      } else {
+        createContinuousQueryOperator.setEveryInterval(
+            createContinuousQueryOperator.getForInterval());
+      }
+    } else {
+      if (createContinuousQueryOperator.getForInterval() == 0) {
+        createContinuousQueryOperator.setForInterval(
+            createContinuousQueryOperator.getEveryInterval());
+      }
+    }
+
+    return createContinuousQueryOperator;
+  }
+
+  private String getFullText(ParserRuleContext context) {
+    if (context.start == null
+        || context.stop == null
+        || context.start.getStartIndex() < 0
+        || context.stop.getStopIndex() < 0) return context.getText();
+
+    return context
+        .start
+        .getInputStream()
+        .getText(Interval.of(context.start.getStartIndex(), 
context.stop.getStopIndex()));
+  }
+
+  public void parseResampleClause(
+      SqlBaseParser.ResampleClauseContext ctx, CreateContinuousQueryOperator 
operator) {
+
+    if (ctx.DURATION().size() == 1) {
+      if (ctx.EVERY() != null) {
+        operator.setEveryInterval(parseDuration(ctx.DURATION(0).getText()));
+      } else if (ctx.FOR() != null) {
+        operator.setForInterval(parseDuration(ctx.DURATION(0).getText()));
+      }
+    } else if (ctx.DURATION().size() == 2) {
+      operator.setEveryInterval(parseDuration(ctx.DURATION(0).getText()));
+      operator.setForInterval(parseDuration(ctx.DURATION(1).getText()));
+    }
+  }
+
+  public void parseCqSelectIntoClause(
+      SqlBaseParser.CqSelectIntoClauseContext ctx,
+      CreateContinuousQueryOperator createContinuousQueryOperator) {
+
+    QueryOperator queryOperator = new QueryOperator(SQLConstant.TOK_QUERY);
+
+    SelectOperator selectOp = (SelectOperator) visit(ctx.selectElements());
+    if (selectOp.getSuffixPaths().size() > 1) {
+      throw new SQLParserException("Cq Select Into currently does not support 
multiple suffixes.");
+    }
+    queryOperator.setSelectOperator(selectOp);
+
+    FromOperator fromOp = (FromOperator) visit(ctx.fromClause());
+    if (fromOp.getPrefixPaths().size() > 1) {
+      throw new SQLParserException("Cq Select Into currently does not support 
multiple prefixes.");
+    }
+    queryOperator.setFromOperator(fromOp);
+
+    if (ctx.whereClause() != null) {
+      Operator operator = visit(ctx.whereClause());
+      if (operator instanceof FilterOperator) {
+        FilterOperator whereOp = (FilterOperator) operator;
+        queryOperator.setFilterOperator(whereOp.getChildren().get(0));
+      }
+    }
+
+    parseCqGroupByTimeClause(ctx.cqGroupByTimeClause(), queryOperator);
+
+    int fromLen = 
queryOperator.getFromOperator().getPrefixPaths().get(0).getNodeLength();
+    if (queryOperator.getLevel() >= fromLen) {
+      throw new SQLParserException(
+          "Cq Select Into: Level should not exceed the <from_prefix> length.");
+    }
+
+    PartialPath targetPath = null;
+    if (ctx.fullPath() != null) {
+      targetPath = parseFullPath(ctx.fullPath());
+    } else if (ctx.suffixPath() != null) {
+      List<String> targetNodes = new ArrayList<>();
+      int trueLevel = queryOperator.getLevel();
+      if (trueLevel == -1) {
+        trueLevel = fromLen - 1;
+      }
+      for (int i = 0; i <= trueLevel; i++) {
+        targetNodes.add("${" + i + "}");
+      }
+      targetNodes.add(ctx.suffixPath().getText());
+      targetPath = new PartialPath(targetNodes.toArray(new String[0]));
+    }
+
+    createContinuousQueryOperator.setTargetPath(targetPath);
+    createContinuousQueryOperator.setQueryOperator(queryOperator);
+  }
+
+  public void parseCqGroupByTimeClause(
+      SqlBaseParser.CqGroupByTimeClauseContext ctx, QueryOperator 
queryOperator) {
+    queryOperator.setGroupByTime(true);
+    queryOperator.setLeftCRightO(true);
+
+    queryOperator.setUnit(parseDuration(ctx.DURATION().getText()));
+    queryOperator.setSlidingStep(queryOperator.getUnit());
+
+    if (ctx.LEVEL() != null && ctx.INT() != null) {
+      queryOperator.setGroupByLevel(true);
+      queryOperator.setLevel(Integer.parseInt(ctx.INT().getText()));
+    }
+  }
+
+  @Override
+  public Operator visitResampleClause(SqlBaseParser.ResampleClauseContext ctx) 
{
+    return visitChildren(ctx);
+  }
+
+  @Override
+  public Operator 
visitCqSelectIntoClause(SqlBaseParser.CqSelectIntoClauseContext ctx) {
+    return visitChildren(ctx);
+  }
+
+  @Override
+  public Operator 
visitCqGroupByTimeClause(SqlBaseParser.CqGroupByTimeClauseContext ctx) {
+    return visitChildren(ctx);
+  }
+
+  @Override
   public Operator visitAggregationElement(AggregationElementContext ctx) {
     SelectOperator selectOp = new SelectOperator(SQLConstant.TOK_SELECT, 
zoneId);
 
@@ -1077,7 +1266,7 @@ public class IoTDBSqlVisitor extends 
SqlBaseBaseVisitor<Operator> {
     SelectOperator selectOp = new SelectOperator(SQLConstant.TOK_SELECT, 
zoneId);
     selectOp.setLastQuery();
     LastClauseContext lastClauseContext = ctx.lastClause();
-    if (lastClauseContext.asClause().size() != 0) {
+    if (!lastClauseContext.asClause().isEmpty()) {
       parseAsClause(lastClauseContext.asClause(), selectOp);
     } else {
       List<SuffixPathContext> suffixPaths = lastClauseContext.suffixPath();
diff --git 
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java 
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
index dd514d3..8514b3d 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/qp/strategy/PhysicalGenerator.java
@@ -33,6 +33,8 @@ import org.apache.iotdb.db.qp.logical.crud.DeleteDataOperator;
 import org.apache.iotdb.db.qp.logical.crud.FilterOperator;
 import org.apache.iotdb.db.qp.logical.crud.InsertOperator;
 import org.apache.iotdb.db.qp.logical.crud.QueryOperator;
+import org.apache.iotdb.db.qp.logical.sys.CreateContinuousQueryOperator;
+import org.apache.iotdb.db.qp.logical.sys.DropContinuousQueryOperator;
 import org.apache.iotdb.db.qp.logical.sys.AlterTimeSeriesOperator;
 import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
 import org.apache.iotdb.db.qp.logical.sys.CountOperator;
@@ -83,6 +85,9 @@ import org.apache.iotdb.db.qp.physical.crud.QueryIndexPlan;
 import org.apache.iotdb.db.qp.physical.crud.QueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.RawDataQueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.ShowContinuousQueriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AlterTimeSeriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AuthorPlan;
 import org.apache.iotdb.db.qp.physical.sys.ClearCachePlan;
@@ -391,6 +396,21 @@ public class PhysicalGenerator {
         return new StartTriggerPlan(((StartTriggerOperator) 
operator).getTriggerName());
       case STOP_TRIGGER:
         return new StopTriggerPlan(((StopTriggerOperator) 
operator).getTriggerName());
+      case CREATE_CONTINUOUS_QUERY:
+        CreateContinuousQueryOperator createContinuousQueryOperator =
+            (CreateContinuousQueryOperator) operator;
+        return new CreateContinuousQueryPlan(
+            createContinuousQueryOperator.getQuerySql(),
+            createContinuousQueryOperator.getContinuousQueryName(),
+            createContinuousQueryOperator.getTargetPath(),
+            createContinuousQueryOperator.getEveryInterval(),
+            createContinuousQueryOperator.getForInterval(),
+            createContinuousQueryOperator.getQueryOperator());
+      case DROP_CONTINUOUS_QUERY:
+        return new DropContinuousQueryPlan(
+            ((DropContinuousQueryOperator) operator).getContinuousQueryName());
+      case SHOW_CONTINUOUS_QUERIES:
+        return new ShowContinuousQueriesPlan();
       default:
         throw new LogicalOperatorException(operator.getType().toString(), "");
     }
diff --git 
a/server/src/main/java/org/apache/iotdb/db/query/dataset/ShowContinuousQueriesResult.java
 
b/server/src/main/java/org/apache/iotdb/db/query/dataset/ShowContinuousQueriesResult.java
new file mode 100644
index 0000000..4fb831b
--- /dev/null
+++ 
b/server/src/main/java/org/apache/iotdb/db/query/dataset/ShowContinuousQueriesResult.java
@@ -0,0 +1,84 @@
+/*
+ * 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;
+
+import org.apache.iotdb.db.metadata.PartialPath;
+
+public class ShowContinuousQueriesResult extends ShowResult {
+
+  private String querySql;
+  private String continuousQueryName;
+  private PartialPath targetPath;
+  private long everyInterval;
+  private long forInterval;
+
+  public ShowContinuousQueriesResult(
+      String querySql,
+      String continuousQueryName,
+      PartialPath targetPath,
+      long everyInterval,
+      long forInterval) {
+    this.querySql = querySql;
+    this.continuousQueryName = continuousQueryName;
+    this.targetPath = targetPath;
+    this.everyInterval = everyInterval;
+    this.forInterval = forInterval;
+  }
+
+  public String getQuerySql() {
+    return querySql;
+  }
+
+  public void setQuerySql(String querySql) {
+    this.querySql = querySql;
+  }
+
+  public String getContinuousQueryName() {
+    return continuousQueryName;
+  }
+
+  public void setContinuousQueryName(String continuousQueryName) {
+    this.continuousQueryName = continuousQueryName;
+  }
+
+  public PartialPath getTargetPath() {
+    return targetPath;
+  }
+
+  public void setTargetPath(PartialPath targetPath) {
+    this.targetPath = targetPath;
+  }
+
+  public long getEveryInterval() {
+    return everyInterval;
+  }
+
+  public void setEveryInterval(long everyInterval) {
+    this.everyInterval = everyInterval;
+  }
+
+  public long getForInterval() {
+    return forInterval;
+  }
+
+  public void setForInterval(long forInterval) {
+    this.forInterval = forInterval;
+  }
+}
diff --git 
a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByTimeDataSet.java
 
b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByTimeDataSet.java
index 0c61934..8048c55 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByTimeDataSet.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/query/dataset/groupby/GroupByTimeDataSet.java
@@ -19,7 +19,9 @@
 
 package org.apache.iotdb.db.query.dataset.groupby;
 
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.qp.physical.crud.GroupByTimePlan;
 import org.apache.iotdb.db.query.aggregation.AggregateResult;
 import org.apache.iotdb.db.query.context.QueryContext;
@@ -80,8 +82,13 @@ public class GroupByTimeDataSet extends QueryDataSet {
 
     this.dataTypes = new ArrayList<>();
     this.paths = new ArrayList<>();
-    for (int i = 0; i < finalPaths.size(); i++) {
-      this.dataTypes.add(TSDataType.INT64);
+    for (Map.Entry<String, AggregateResult> entry : finalPaths.entrySet()) {
+      try {
+        this.paths.add(new PartialPath(entry.getKey()));
+      } catch (IllegalPathException e) {
+        e.printStackTrace();
+      }
+      this.dataTypes.add(entry.getValue().getResultDataType());
     }
   }
 
diff --git 
a/server/src/main/java/org/apache/iotdb/db/query/udf/core/context/UDFContext.java
 
b/server/src/main/java/org/apache/iotdb/db/query/udf/core/context/UDFContext.java
index 00e294e..2c95e7f 100644
--- 
a/server/src/main/java/org/apache/iotdb/db/query/udf/core/context/UDFContext.java
+++ 
b/server/src/main/java/org/apache/iotdb/db/query/udf/core/context/UDFContext.java
@@ -19,6 +19,7 @@
 
 package org.apache.iotdb.db.query.udf.core.context;
 
+import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.exception.metadata.MetadataException;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.service.IoTDB;
@@ -139,4 +140,31 @@ public class UDFContext {
     }
     return columnParameterPart;
   }
+
+  public UDFContext copy() {
+
+    UDFContext ret = new UDFContext(this.name);
+
+    for (Map.Entry<String, String> attribute : attributes.entrySet()) {
+      ret.attributes.put(attribute.getKey(), attribute.getValue());
+    }
+
+    try {
+      for (PartialPath path : paths) {
+        ret.paths.add(new PartialPath(path.getFullPath()));
+      }
+    } catch (IllegalPathException e) {
+      e.printStackTrace();
+    }
+
+    for (TSDataType type : dataTypes) {
+      ret.dataTypes.add(type);
+    }
+
+    ret.columnParameterPart = this.columnParameterPart;
+
+    ret.column = this.column;
+
+    return ret;
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java 
b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
index 6a516db..7b2ad93 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/IoTDB.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.db.conf.IoTDBConfigCheck;
 import org.apache.iotdb.db.conf.IoTDBConstant;
 import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import org.apache.iotdb.db.cost.statistic.Measurement;
+import org.apache.iotdb.db.cq.ContinuousQueryService;
 import org.apache.iotdb.db.engine.StorageEngine;
 import org.apache.iotdb.db.engine.cache.CacheHitRatioMonitor;
 import org.apache.iotdb.db.engine.compaction.CompactionMergeTaskPoolManager;
@@ -114,6 +115,9 @@ public class IoTDB implements IoTDBMBean {
     registerManager.register(UDFClassLoaderManager.getInstance());
     registerManager.register(UDFRegistrationService.getInstance());
 
+    registerManager.register(ContinuousQueryService.getInstance());
+
+    registerManager.register(RPCService.getInstance());
     // in cluster mode, RPC service is not enabled.
     if (IoTDBDescriptor.getInstance().getConfig().isEnableRpcService()) {
       registerManager.register(RPCService.getInstance());
diff --git a/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java 
b/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
index b474b2c..b61ee22 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/ServiceType.java
@@ -50,7 +50,8 @@ public enum ServiceType {
 
   FLUSH_SERVICE(
       "Flush ServerService", 
generateJmxName("org.apache.iotdb.db.engine.pool", "Flush Manager")),
-  CLUSTER_MONITOR_SERVICE("Cluster Monitor ServerService", "Cluster Monitor");
+  CLUSTER_MONITOR_SERVICE("Cluster Monitor ServerService", "Cluster Monitor"),
+  CONTINUOUS_QUERY_SERVICE("Continuous Query Service", "Continuous Query 
Service");
 
   private final String name;
   private final String jmxName;
diff --git 
a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java 
b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
index 2821aa2..d58b2dc 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/TSServiceImpl.java
@@ -35,7 +35,6 @@ import 
org.apache.iotdb.db.exception.QueryInBatchStatementException;
 import org.apache.iotdb.db.exception.StorageEngineException;
 import org.apache.iotdb.db.exception.metadata.IllegalPathException;
 import org.apache.iotdb.db.exception.metadata.MetadataException;
-import org.apache.iotdb.db.exception.metadata.StorageGroupNotSetException;
 import org.apache.iotdb.db.exception.query.QueryProcessException;
 import org.apache.iotdb.db.exception.query.QueryTimeoutRuntimeException;
 import org.apache.iotdb.db.exception.runtime.SQLParserException;
@@ -1235,7 +1234,7 @@ public class TSServiceImpl implements TSIService.Iface, 
ServerContext {
   }
 
   private boolean executeNonQuery(PhysicalPlan plan)
-      throws QueryProcessException, StorageGroupNotSetException, 
StorageEngineException {
+      throws QueryProcessException, MetadataException, StorageEngineException {
     if (IoTDBDescriptor.getInstance().getConfig().isReadOnly()) {
       throw new QueryProcessException(
           "Current system mode is read-only, does not support non-query 
operation");
diff --git 
a/server/src/main/java/org/apache/iotdb/db/tools/mlog/MLogParser.java 
b/server/src/main/java/org/apache/iotdb/db/tools/mlog/MLogParser.java
index 85af420..1be3cef 100644
--- a/server/src/main/java/org/apache/iotdb/db/tools/mlog/MLogParser.java
+++ b/server/src/main/java/org/apache/iotdb/db/tools/mlog/MLogParser.java
@@ -22,14 +22,7 @@ import org.apache.iotdb.db.metadata.MLogTxtWriter;
 import org.apache.iotdb.db.metadata.PartialPath;
 import org.apache.iotdb.db.metadata.logfile.MLogReader;
 import org.apache.iotdb.db.qp.physical.PhysicalPlan;
-import org.apache.iotdb.db.qp.physical.sys.ChangeAliasPlan;
-import org.apache.iotdb.db.qp.physical.sys.ChangeTagOffsetPlan;
-import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan;
-import org.apache.iotdb.db.qp.physical.sys.MNodePlan;
-import org.apache.iotdb.db.qp.physical.sys.MeasurementMNodePlan;
-import org.apache.iotdb.db.qp.physical.sys.SetStorageGroupPlan;
-import org.apache.iotdb.db.qp.physical.sys.SetTTLPlan;
-import org.apache.iotdb.db.qp.physical.sys.StorageGroupMNodePlan;
+import org.apache.iotdb.db.qp.physical.sys.*;
 
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.CommandLineParser;
@@ -198,6 +191,12 @@ public class MLogParser {
           case MNODE:
             mLogTxtWriter.serializeMNode((MNodePlan) plan);
             break;
+          case CREATE_CONTINUOUS_QUERY:
+            mLogTxtWriter.createContinuousQuery((CreateContinuousQueryPlan) 
plan);
+            break;
+          case DROP_CONTINUOUS_QUERY:
+            mLogTxtWriter.dropContinuousQuery((DropContinuousQueryPlan) plan);
+            break;
           default:
             logger.warn("unknown plan {}", plan);
         }
diff --git 
a/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateContinuousQueryIT.java
 
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateContinuousQueryIT.java
new file mode 100644
index 0000000..a2e38c4
--- /dev/null
+++ 
b/server/src/test/java/org/apache/iotdb/db/integration/IoTDBCreateContinuousQueryIT.java
@@ -0,0 +1,400 @@
+/*
+ * 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.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.sql.*;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+/**
+ * Notice that, all test begins with "IoTDB" is integration test. All test 
which will start the
+ * IoTDB server should be defined as integration test.
+ */
+public class IoTDBCreateContinuousQueryIT {
+  private Statement statement;
+  private Connection connection;
+  private final ExecutorService pool = Executors.newCachedThreadPool();
+
+  String[] timeSeriesArray = {
+    "root.ln.wf01.wt01.ws01",
+    "root.ln.wf01.wt01.ws02",
+    "root.ln.wf01.wt02.ws01",
+    "root.ln.wf01.wt02.ws02",
+    "root.ln.wf02.wt01.ws01",
+    "root.ln.wf02.wt01.ws02",
+    "root.ln.wf02.wt02.ws01",
+    "root.ln.wf02.wt02.ws02"
+  };
+
+  @Before
+  public void setUp() throws Exception {
+
+    EnvironmentUtils.envSetUp();
+
+    Class.forName(Config.JDBC_DRIVER_NAME);
+    connection = DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", 
"root", "root");
+    statement = connection.createStatement();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    pool.shutdownNow();
+    statement.close();
+    connection.close();
+    EnvironmentUtils.cleanEnv();
+  }
+
+  @Test
+  public void testCreateContinuousQuery1() throws Exception {
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq1 "
+            + "BEGIN SELECT max_value(temperature) INTO temperature_max FROM 
root.ln.*.*.* "
+            + "GROUP BY time(10s) END");
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq2 "
+            + "BEGIN SELECT count(temperature) INTO temperature_cnt FROM 
root.ln.wf01.*.* "
+            + "WHERE temperature > 80.0 GROUP BY time(10s), level=3 END");
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq3 "
+            + "RESAMPLE EVERY 20s FOR 20s "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* "
+            + "GROUP BY time(10s), level=2 END");
+
+    statement.execute("DROP CONTINUOUS QUERY cq1");
+    statement.execute("DROP CONTINUOUS QUERY cq2");
+
+    createCreateContinuousQuery1Tool(new String[] {"cq3"});
+
+    EnvironmentUtils.stopDaemon();
+    setUp();
+
+    createCreateContinuousQuery1Tool(new String[] {"cq3"});
+  }
+
+  private void createCreateContinuousQuery1Tool(String[] continuousQueryArray) 
throws SQLException {
+    boolean hasResult = statement.execute("show continuous queries");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String cq = resultSet.getString("cq name");
+        resultList.add(cq);
+      }
+    }
+    Assert.assertEquals(continuousQueryArray.length, resultList.size());
+
+    List<String> collect =
+        resultList.stream()
+            .sorted(Comparator.comparingInt(e -> e.split("\\.").length))
+            .collect(Collectors.toList());
+
+    for (String s : continuousQueryArray) {
+      Assert.assertTrue(collect.contains(s));
+    }
+  }
+
+  @Test
+  public void testCreateContinuousQuery2() throws Exception {
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "RESAMPLE EVERY 1s FOR 1s "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(10000);
+
+    boolean hasResult = statement.execute("select temperature_avg from 
root.ln.wf01");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timestamp = resultSet.getString("Time");
+        System.out.println(timestamp);
+        resultList.add(timestamp);
+      }
+    }
+    Assert.assertEquals(10000 / 1000, resultList.size());
+  }
+
+  @Test
+  public void testCreateContinuousQuery3() throws Exception {
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "RESAMPLE EVERY 20s "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(40000);
+
+    boolean hasResult = statement.execute("select temperature_avg from 
root.ln.wf01");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timestamp = resultSet.getString("Time");
+        System.out.println(timestamp);
+        resultList.add(timestamp);
+      }
+    }
+    Assert.assertEquals(40000 / 1000, resultList.size());
+  }
+
+  @Test
+  public void testCreateContinuousQuery4() throws Exception {
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(10000);
+
+    boolean hasResult = statement.execute("select temperature_avg from 
root.ln.wf01");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timestamp = resultSet.getString("Time");
+        System.out.println(timestamp);
+        resultList.add(timestamp);
+      }
+    }
+    Assert.assertEquals(10000 / 1000, resultList.size());
+  }
+
+  @Test
+  public void testCreateContinuousQuery5() throws Exception {
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* WHERE temperature < 0 "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(10000);
+
+    boolean hasResult = statement.execute("select temperature_avg from 
root.ln.wf01");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timestamp = resultSet.getString("Time");
+        System.out.println(timestamp);
+        resultList.add(timestamp);
+      }
+    }
+    Assert.assertEquals(0, resultList.size());
+  }
+
+  @Test
+  public void testCreateContinuousQuery6() throws Exception {
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "BEGIN SELECT count(temperature) INTO temperature_cnt FROM 
root.ln.*.*.* "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(10000);
+
+    checkTimeSeries(
+        new String[] {
+          "root.ln.wf01.wt01.ws01.temperature",
+          "root.ln.wf01.wt01.ws02.temperature",
+          "root.ln.wf01.wt02.ws01.temperature",
+          "root.ln.wf01.wt02.ws02.temperature",
+          "root.ln.wf02.wt01.ws01.temperature",
+          "root.ln.wf02.wt01.ws02.temperature",
+          "root.ln.wf02.wt02.ws01.temperature",
+          "root.ln.wf02.wt02.ws02.temperature",
+          "root.ln.wf01.temperature_cnt",
+          "root.ln.wf02.temperature_cnt"
+        });
+  }
+
+  @Test
+  public void testCreateContinuousQuery7() throws Exception {
+
+    statement.execute(
+        "CREATE CONTINUOUS QUERY cq "
+            + "BEGIN SELECT avg(temperature) INTO temperature_avg FROM 
root.ln.wf01.*.* "
+            + "GROUP BY time(1s), level=2 END");
+
+    Thread.sleep(10000);
+
+    for (String timeSeries : timeSeriesArray) {
+      statement.execute(
+          String.format(
+              "create timeseries %s.temperature with 
datatype=FLOAT,encoding=RLE", timeSeries));
+    }
+
+    Connection producerConnection =
+        DriverManager.getConnection("jdbc:iotdb://127.0.0.1:6667/", "root", 
"root");
+    pool.execute(new Producer(producerConnection));
+
+    Thread.sleep(10000);
+
+    boolean hasResult = statement.execute("select temperature_avg from 
root.ln.wf01");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timestamp = resultSet.getString("Time");
+        System.out.println(timestamp);
+        resultList.add(timestamp);
+      }
+    }
+    Assert.assertEquals(10000 / 1000, resultList.size());
+  }
+
+  private void checkTimeSeries(String[] timeSeriesArray) throws SQLException {
+    boolean hasResult = statement.execute("show timeseries");
+    Assert.assertTrue(hasResult);
+
+    List<String> resultList = new ArrayList<>();
+    try (ResultSet resultSet = statement.getResultSet()) {
+      while (resultSet.next()) {
+        String timeseries = resultSet.getString("timeseries");
+        resultList.add(timeseries);
+      }
+    }
+    Assert.assertEquals(timeSeriesArray.length, resultList.size());
+
+    List<String> collect =
+        resultList.stream()
+            .sorted(Comparator.comparingInt(e -> e.split("\\.").length))
+            .collect(Collectors.toList());
+
+    for (String s : timeSeriesArray) {
+      Assert.assertTrue(collect.contains(s));
+    }
+  }
+
+  class Producer implements Runnable {
+    private final Statement producerStatement;
+    private final Connection producerConnection;
+
+    public Producer(Connection producerConnection) throws SQLException {
+      this.producerConnection = producerConnection;
+      this.producerStatement = producerConnection.createStatement();
+    }
+
+    protected void finalize() throws SQLException {
+      producerStatement.close();
+      producerConnection.close();
+    }
+
+    @Override
+    public void run() {
+
+      while (!Thread.currentThread().isInterrupted()) {
+        try {
+          Thread.sleep(100);
+          for (String timeSeries : timeSeriesArray) {
+            this.producerStatement.execute(
+                String.format(
+                    "insert into %s(timestamp, temperature) values(now(), 
%.3f)",
+                    timeSeries, 200 * Math.random()));
+          }
+        } catch (InterruptedException e) {
+          Thread.currentThread().interrupt();
+        } catch (SQLException throwables) {
+          throwables.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 e6aaa95..c4ac722 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
@@ -37,6 +37,9 @@ import org.apache.iotdb.db.qp.physical.crud.LastQueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.QueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.RawDataQueryPlan;
 import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
+import org.apache.iotdb.db.qp.physical.sys.CreateContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.DropContinuousQueryPlan;
+import org.apache.iotdb.db.qp.physical.sys.ShowContinuousQueriesPlan;
 import org.apache.iotdb.db.qp.physical.sys.AuthorPlan;
 import org.apache.iotdb.db.qp.physical.sys.CreateFunctionPlan;
 import org.apache.iotdb.db.qp.physical.sys.CreateTimeSeriesPlan;
@@ -1215,4 +1218,125 @@ public class PhysicalPlanTest {
     Assert.assertTrue(plan.isQuery());
     Assert.assertEquals("root.sg1.d1.s1", plan.getPath().getFullPath());
   }
+
+  @Test
+  public void testCreateCQ1() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 BEGIN SELECT max_value(temperature) INTO 
temperature_max FROM root.ln.*.*.* GROUP BY time(10s) END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(10000, plan.getEveryInterval());
+    Assert.assertEquals(10000, plan.getForInterval());
+    Assert.assertEquals(
+        "${0}.${1}.${2}.${3}.${4}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* group by ([now() - 
10s, now()), 10s)",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testCreateCQ2() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 BEGIN SELECT max_value(temperature) INTO 
temperature_max FROM root.ln.*.*.* GROUP BY time(10s), level = 3 END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(10000, plan.getEveryInterval());
+    Assert.assertEquals(10000, plan.getForInterval());
+    Assert.assertEquals("${0}.${1}.${2}.${3}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* group by ([now() - 
10s, now()), 10s), level = 3",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testCreateCQ3() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 RESAMPLE EVERY 20s BEGIN SELECT 
max_value(temperature) INTO temperature_max FROM root.ln.*.*.* GROUP BY 
time(10s), level = 3 END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(20000, plan.getEveryInterval());
+    Assert.assertEquals(20000, plan.getForInterval());
+    Assert.assertEquals("${0}.${1}.${2}.${3}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* group by ([now() - 
10s, now()), 10s), level = 3",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testCreateCQ4() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 RESAMPLE EVERY 20s FOR 10s BEGIN SELECT 
max_value(temperature) INTO temperature_max FROM root.ln.*.*.* GROUP BY 
time(10s), level = 3 END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(20000, plan.getEveryInterval());
+    Assert.assertEquals(10000, plan.getForInterval());
+    Assert.assertEquals("${0}.${1}.${2}.${3}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* group by ([now() - 
10s, now()), 10s), level = 3",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testCreateCQ5() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 RESAMPLE FOR 20s BEGIN SELECT 
max_value(temperature) INTO temperature_max FROM root.ln.*.*.* GROUP BY 
time(10s), level = 3 END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(20000, plan.getEveryInterval());
+    Assert.assertEquals(20000, plan.getForInterval());
+    Assert.assertEquals("${0}.${1}.${2}.${3}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* group by ([now() - 
20s, now()), 10s), level = 3",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testCreateCQ6() throws QueryProcessException {
+    String sql =
+        "CREATE CONTINUOUS QUERY cq1 RESAMPLE FOR 20s BEGIN SELECT 
max_value(temperature) INTO temperature_max FROM root.ln.*.*.* WHERE 
temperature > 50 and temperature < 100 GROUP BY time(10s), level = 3 END";
+
+    CreateContinuousQueryPlan plan =
+        (CreateContinuousQueryPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+    Assert.assertEquals(20000, plan.getEveryInterval());
+    Assert.assertEquals(20000, plan.getForInterval());
+    Assert.assertEquals("${0}.${1}.${2}.${3}.temperature_max", 
plan.getTargetPath().getFullPath());
+    Assert.assertEquals(
+        "select max_value(temperature) from root.ln.*.*.* where temperature > 
50 and temperature < 100 group by ([now() - 20s, now()), 10s), level = 3",
+        plan.getQuerySql());
+  }
+
+  @Test
+  public void testDropCQ() throws QueryProcessException {
+    String sql = "DROP CONTINUOUS QUERY cq1";
+
+    DropContinuousQueryPlan plan = (DropContinuousQueryPlan) 
processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertFalse(plan.isQuery());
+    Assert.assertEquals("cq1", plan.getContinuousQueryName());
+  }
+
+  @Test
+  public void testShowCQs() throws QueryProcessException {
+    String sql = "SHOW CONTINUOUS QUERIES";
+
+    ShowContinuousQueriesPlan plan =
+        (ShowContinuousQueriesPlan) processor.parseSQLToPhysicalPlan(sql);
+    Assert.assertTrue(plan.isQuery());
+  }
 }
diff --git a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java 
b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
index f2fd3c1..ed695b1 100644
--- a/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
+++ b/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
@@ -46,6 +46,8 @@ public enum TSStatusCode {
   LOAD_FILE_ERROR(316),
   STORAGE_GROUP_NOT_READY(317),
   ILLEGAL_PARAMETER(318),
+  CONTINUOUS_QUERY_ALREADY_EXIST(319),
+  CONTINUOUS_QUERY_NOT_EXIST(320),
 
   EXECUTE_STATEMENT_ERROR(400),
   SQL_PARSE_ERROR(401),

Reply via email to