You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by hu...@apache.org on 2022/10/26 15:19:42 UTC

[iotdb] branch IOTDB-4619 updated: fix datanode

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

hui pushed a commit to branch IOTDB-4619
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/IOTDB-4619 by this push:
     new 8377966fee fix datanode
8377966fee is described below

commit 8377966fee917ac098f8f535f77fbe65e98a644a
Author: liuminghui233 <54...@qq.com>
AuthorDate: Wed Oct 26 23:14:10 2022 +0800

    fix datanode
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   |   4 +-
 .../org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java   |  26 ++-
 .../java/org/apache/iotdb/db/it/cq/IoTDBCQIT.java  | 193 ++++++++++++++++++---
 .../config/executor/ClusterConfigTaskExecutor.java |   8 +-
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       |  15 +-
 .../plan/statement/component/FillComponent.java    |  12 ++
 .../plan/statement/component/FromComponent.java    |  12 ++
 .../statement/component/GroupByLevelComponent.java |  17 ++
 .../statement/component/GroupByTimeComponent.java  |  35 ++++
 .../plan/statement/component/HavingCondition.java  |   4 +
 .../plan/statement/component/IntoComponent.java    |  12 ++
 .../db/mpp/plan/statement/component/IntoItem.java  |  15 ++
 .../plan/statement/component/OrderByComponent.java |  12 ++
 .../plan/statement/component/SelectComponent.java  |  21 ++-
 .../db/mpp/plan/statement/component/SortItem.java  |   4 +
 .../plan/statement/component/WhereCondition.java   |   4 +
 .../db/mpp/plan/statement/crud/QueryStatement.java |  57 +++++-
 .../mpp/plan/statement/literal/BooleanLiteral.java |   5 +
 .../mpp/plan/statement/literal/DoubleLiteral.java  |   5 +
 .../db/mpp/plan/statement/literal/LongLiteral.java |   5 +
 .../db/mpp/plan/statement/literal/NullLiteral.java |   5 +
 .../mpp/plan/statement/literal/StringLiteral.java  |   5 +
 .../metadata/CreateContinuousQueryStatement.java   |  71 +++++---
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    |   3 +-
 .../impl/DataNodeInternalRPCServiceImpl.java       |   6 +-
 25 files changed, 479 insertions(+), 77 deletions(-)

diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index 379b3675bc..6a4d90583b 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -164,8 +164,8 @@ resampleClause
     : RESAMPLE
         (EVERY everyInterval=DURATION_LITERAL)?
         (FOR DURATION_LITERAL)?
-        (BOUNDARY boundaryTime=dateExpression)?
-        (RANGE startTimeOffset=DURATION_LITERAL (COMMA endTimeOffset=DURATION_LITERAL))?
+        (BOUNDARY boundaryTime=timeValue)?
+        (RANGE startTimeOffset=DURATION_LITERAL (COMMA endTimeOffset=DURATION_LITERAL)?)?
     ;
 
 timeoutPolicyClause
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java
index 83bb55c157..48d1b404ff 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQExecIT.java
@@ -24,7 +24,6 @@ import org.apache.iotdb.itbase.category.ClusterIT;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -40,7 +39,6 @@ import static org.junit.Assert.fail;
 
 @RunWith(IoTDBTestRunner.class)
 @Category(ClusterIT.class)
-@Ignore
 public class IoTDBCQExecIT {
 
   @BeforeClass
@@ -93,8 +91,7 @@ public class IoTDBCQExecIT {
       statement.execute(
           "CREATE CONTINUOUS QUERY cq1\n"
               + "RESAMPLE EVERY 2s\n"
-              + "BBOUNDARY "
-              + firstExecutionTime
+              + String.format("BOUNDARY %d\n", firstExecutionTime)
               + "BEGIN \n"
               + "  SELECT max_value(s1) \n"
               + "  INTO root.sg.d1(s1_max)\n"
@@ -170,9 +167,9 @@ public class IoTDBCQExecIT {
 
       statement.execute(
           "CREATE CONTINUOUS QUERY cq2\n"
-              + "RESAMPLE RANGE 4s\n"
-              + "BBOUNDARY "
-              + firstExecutionTime
+              + "RESAMPLE \n"
+              + String.format("BOUNDARY %d\n", firstExecutionTime)
+              + "RANGE 4s \n"
               + "BEGIN \n"
               + "  SELECT max_value(s1) \n"
               + "  INTO root.sg.d2(s1_max)\n"
@@ -248,9 +245,9 @@ public class IoTDBCQExecIT {
 
       statement.execute(
           "CREATE CONTINUOUS QUERY cq3\n"
-              + "RESAMPLE EVERY 20s RANGE 40s\n"
-              + "BBOUNDARY "
-              + firstExecutionTime
+              + "RESAMPLE EVERY 20s\n"
+              + String.format("BOUNDARY %d\n", firstExecutionTime)
+              + "RANGE 40s\n"
               + "BEGIN \n"
               + "  SELECT max_value(s1) \n"
               + "  INTO root.sg.d3(s1_max)\n"
@@ -332,9 +329,9 @@ public class IoTDBCQExecIT {
 
       statement.execute(
           "CREATE CONTINUOUS QUERY cq4\n"
-              + "RESAMPLE EVERY 20s RANGE 20s, 10s\n"
-              + "BBOUNDARY "
-              + firstExecutionTime
+              + "RESAMPLE EVERY 20s\n"
+              + String.format("BOUNDARY %d\n", firstExecutionTime)
+              + "RANGE 20s, 10s\n"
               + "BEGIN \n"
               + "  SELECT max_value(s1) \n"
               + "  INTO root.sg.d4(s1_max)\n"
@@ -409,8 +406,7 @@ public class IoTDBCQExecIT {
       statement.execute(
           "CREATE CONTINUOUS QUERY cq5\n"
               + "RESAMPLE EVERY 2s\n"
-              + "BBOUNDARY "
-              + firstExecutionTime
+              + String.format("BOUNDARY %d\n", firstExecutionTime)
               + "BEGIN \n"
               + "  SELECT s1 + 1 \n"
               + "  INTO root.sg.d5(precalculated_s1)\n"
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQIT.java
index 49a4853190..7910ffeae5 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/cq/IoTDBCQIT.java
@@ -24,7 +24,6 @@ import org.apache.iotdb.itbase.category.ClusterIT;
 
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
-import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.experimental.categories.Category;
 import org.junit.runner.RunWith;
@@ -34,11 +33,11 @@ import java.sql.ResultSet;
 import java.sql.Statement;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.fail;
 
 @RunWith(IoTDBTestRunner.class)
 @Category(ClusterIT.class)
-@Ignore
 public class IoTDBCQIT {
 
   @BeforeClass
@@ -70,7 +69,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: Specifying time range in GROUP BY TIME clause is prohibited.",
+            e.getMessage());
       }
 
       // 2. specify time filter in where clause
@@ -87,7 +88,8 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: Specifying time filters in the query body is prohibited.", e.getMessage());
       }
 
       // 3. no every clause meanwhile no group by time
@@ -102,7 +104,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "416: CQ: At least one of the parameters `every_interval` and `group_by_interval` needs to be specified.",
+            e.getMessage());
       }
 
       // 4. no INTO clause
@@ -117,7 +121,7 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals("500: CQ: The query body misses an INTO clause.", e.getMessage());
       }
 
       // 5. EVERY interval is less than continuous_query_min_every_interval_in_ms in
@@ -135,7 +139,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: Every interval [50] should not be lower than the `continuous_query_minimum_every_interval` [1000] configured.",
+            e.getMessage());
       }
 
       // 6. start_time_offset < 0
@@ -152,7 +158,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "401: Error occurred while parsing SQL to physical plan: line 2:15 extraneous input '-' expecting DURATION_LITERAL",
+            e.getMessage());
       }
 
       // 7. start_time_offset == 0
@@ -161,7 +169,7 @@ public class IoTDBCQIT {
             "CREATE CQ s1_count_cq_2\n"
                 + "RESAMPLE RANGE 0m\n"
                 + "BEGIN \n"
-                + "  SELECT count(s1)  \n"
+                + "  SELECT count(s1)\n"
                 + "    INTO root.sg_count.d(count_s1)\n"
                 + "    FROM root.sg.d\n"
                 + "    GROUP BY(30m)\n"
@@ -169,7 +177,7 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals("500: CQ: The start time offset should be greater than 0.", e.getMessage());
       }
 
       // 8. end_time_offset < 0
@@ -186,7 +194,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "401: Error occurred while parsing SQL to physical plan: line 2:20 extraneous input '-' expecting DURATION_LITERAL",
+            e.getMessage());
       }
 
       // 9. end_time_offset == start_time_offset
@@ -203,7 +213,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: The start time offset should be greater than end time offset.",
+            e.getMessage());
       }
 
       // 10. end_time_offset > start_time_offset
@@ -220,7 +232,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: The start time offset should be greater than end time offset.",
+            e.getMessage());
       }
 
       // 11. group_by_interval > start_time_offset
@@ -229,7 +243,7 @@ public class IoTDBCQIT {
             "CREATE CQ s1_count_cq_2\n"
                 + "RESAMPLE RANGE 30m\n"
                 + "BEGIN \n"
-                + "  SELECT count(s1)  \n"
+                + "  SELECT count(s1) \n"
                 + "    INTO root.sg_count.d(count_s1)\n"
                 + "    FROM root.sg.d\n"
                 + "    GROUP BY(1h)\n"
@@ -237,7 +251,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "500: CQ: The start time offset should be greater than or equal to every interval.",
+            e.getMessage());
       }
 
       // 12. TIMEOUT POLICY is not BLOCKED or DISCARD
@@ -255,7 +271,9 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals(
+            "401: Error occurred while parsing SQL to physical plan: line 3:15 mismatched input 'UNKNOWN' expecting {BLOCKED, DISCARD}",
+            e.getMessage());
       }
 
       // 13. create duplicated cq
@@ -272,9 +290,10 @@ public class IoTDBCQIT {
         statement.execute(sql);
         fail();
       } catch (Exception e) {
-        // TODO add assert for error message
+        assertEquals("932: CQ s1_count_cq has already been created.", e.getMessage());
       }
 
+      statement.execute("DROP CQ s1_count_cq;");
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -286,6 +305,7 @@ public class IoTDBCQIT {
     try (Connection connection = EnvFactory.getEnv().getConnection();
         Statement statement = connection.createStatement()) {
 
+      String[] cqIds = {"correct_cq_1", "correct_cq_2", "correct_cq_3", "s1_count_cq_correct"};
       try {
         String sql =
             "CREATE CQ correct_cq_1 \n"
@@ -354,6 +374,10 @@ public class IoTDBCQIT {
         e.printStackTrace();
         fail(e.getMessage());
       }
+
+      for (String cqId : cqIds) {
+        statement.execute(String.format("DROP CQ %s;", cqId));
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -406,6 +430,60 @@ public class IoTDBCQIT {
             + "    GROUP BY(10m)\n"
             + "END"
       };
+      String[] formattedCqSQLs = {
+        "CREATE CQ show_cq_1\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms, 600000ms\n"
+            + "TIMEOUT POLICY BLOCKED\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (1800000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ show_cq_2\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY BLOCKED\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (1800000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ show_cq_3\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 600000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY DISCARD\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (600000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ show_cq_4\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY DISCARD\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (600000ms)\n"
+            + "END\n"
+            + ";"
+      };
 
       for (String sql : cqSQLs) {
         statement.execute(sql);
@@ -416,14 +494,18 @@ public class IoTDBCQIT {
         int cnt = 0;
         while (resultSet.next()) {
           // No need to add time column for aggregation query
-          assertEquals(cqIds[cnt], resultSet.getString(0));
-          assertEquals(cqSQLs[cnt], resultSet.getString(1));
-          assertEquals("ACTIVE", resultSet.getString(2));
+          assertEquals(cqIds[cnt], resultSet.getString(1));
+          assertEquals(formattedCqSQLs[cnt], resultSet.getString(2));
+          assertEquals("ACTIVE", resultSet.getString(3));
           cnt++;
         }
         assertEquals(cqIds.length, cnt);
       }
 
+      for (String cqId : cqIds) {
+        statement.execute(String.format("DROP CQ %s;", cqId));
+      }
+
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
@@ -476,6 +558,60 @@ public class IoTDBCQIT {
             + "    GROUP BY(10m)\n"
             + "END"
       };
+      String[] formattedCqSQLs = {
+        "CREATE CQ drop_cq_1\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms, 600000ms\n"
+            + "TIMEOUT POLICY BLOCKED\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (1800000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ drop_cq_2\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY BLOCKED\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (1800000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ drop_cq_3\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 600000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY DISCARD\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (600000ms)\n"
+            + "END\n"
+            + ";",
+        "CREATE CQ drop_cq_4\n"
+            + "RESAMPLE\n"
+            + "\tEVERY 1800000ms\n"
+            + "\tBOUNDARY 0\n"
+            + "\tRANGE 1800000ms\n"
+            + "TIMEOUT POLICY DISCARD\n"
+            + "BEGIN\n"
+            + "\tSELECT count(s1)\n"
+            + "\t\tINTO root.sg_count.d(count_s1)\n"
+            + "\t\tFROM root.sg.d\n"
+            + "\t\tGROUP BY TIME (600000ms)\n"
+            + "END\n"
+            + ";"
+      };
 
       for (String sql : cqSQLs) {
         statement.execute(sql);
@@ -486,9 +622,9 @@ public class IoTDBCQIT {
         int cnt = 0;
         while (resultSet.next()) {
           // No need to add time column for aggregation query
-          assertEquals(cqIds[cnt], resultSet.getString(0));
-          assertEquals(cqSQLs[cnt], resultSet.getString(1));
-          assertEquals("ACTIVE", resultSet.getString(2));
+          assertEquals(cqIds[cnt], resultSet.getString(1));
+          assertEquals(formattedCqSQLs[cnt], resultSet.getString(2));
+          assertEquals("ACTIVE", resultSet.getString(3));
           cnt++;
         }
         assertEquals(cqIds.length, cnt);
@@ -504,14 +640,19 @@ public class IoTDBCQIT {
         int cnt = 0;
         while (resultSet.next()) {
           // No need to add time column for aggregation query
-          assertEquals(cqIds[resultIndex[cnt]], resultSet.getString(0));
-          assertEquals(cqSQLs[resultIndex[cnt]], resultSet.getString(1));
-          assertEquals("ACTIVE", resultSet.getString(2));
+          assertEquals(cqIds[resultIndex[cnt]], resultSet.getString(1));
+          assertEquals(formattedCqSQLs[resultIndex[cnt]], resultSet.getString(2));
+          assertEquals("ACTIVE", resultSet.getString(3));
           cnt++;
         }
         assertEquals(resultIndex.length, cnt);
       }
 
+      statement.execute("DROP CQ drop_cq_1");
+      statement.execute("DROP CQ drop_cq_4");
+      try (ResultSet resultSet = statement.executeQuery("show CQS")) {
+        assertFalse(resultSet.next());
+      }
     } catch (Exception e) {
       e.printStackTrace();
       fail(e.getMessage());
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
index 7ed50ae64f..de49ae98ea 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/execution/config/executor/ClusterConfigTaskExecutor.java
@@ -1277,6 +1277,10 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor {
   public SettableFuture<ConfigTaskResult> createContinuousQuery(
       CreateContinuousQueryStatement createContinuousQueryStatement) {
     createContinuousQueryStatement.semanticCheck();
+
+    String queryBody = createContinuousQueryStatement.getQueryBody();
+    String sql = createContinuousQueryStatement.getSql();
+    // TODO Do not modify Statement in Analyzer
     Analyzer.validate(createContinuousQueryStatement.getQueryBodyStatement());
 
     SettableFuture<ConfigTaskResult> future = SettableFuture.create();
@@ -1290,8 +1294,8 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor {
               createContinuousQueryStatement.getStartTimeOffset(),
               createContinuousQueryStatement.getEndTimeOffset(),
               createContinuousQueryStatement.getTimeoutPolicy().getType(),
-              createContinuousQueryStatement.getQueryBody(),
-              createContinuousQueryStatement.getSql(),
+              queryBody,
+              sql,
               createContinuousQueryStatement.getZoneId());
       final TSStatus executionStatus = client.createCQ(tCreateCQReq);
       if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != executionStatus.getCode()) {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
index b7f5d08f7e..6cc2487146 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/parser/ASTVisitor.java
@@ -804,10 +804,9 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
   @Override
   public Statement visitCreateContinuousQuery(IoTDBSqlParser.CreateContinuousQueryContext ctx) {
     CreateContinuousQueryStatement statement = new CreateContinuousQueryStatement();
-    statement.setSql(ctx.getText());
+
     statement.setCqId(parseIdentifier(ctx.cqId.getText()));
 
-    statement.setQueryBody(ctx.selectStatement().getText());
     QueryStatement queryBodyStatement =
         (QueryStatement) visitSelectStatement(ctx.selectStatement());
     queryBodyStatement.setCqQueryBody(true);
@@ -815,6 +814,16 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
 
     if (ctx.resampleClause() != null) {
       parseResampleClause(ctx.resampleClause(), statement);
+    } else {
+      QueryStatement queryStatement = statement.getQueryBodyStatement();
+      if (!queryStatement.isGroupByTime()) {
+        throw new SemanticException(
+            "CQ: At least one of the parameters `every_interval` and `group_by_interval` needs to be specified.");
+      }
+
+      long interval = queryStatement.getGroupByTimeComponent().getInterval();
+      statement.setEveryInterval(interval);
+      statement.setStartTimeOffset(interval);
     }
 
     if (ctx.timeoutPolicyClause() != null) {
@@ -843,7 +852,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
     }
 
     if (ctx.BOUNDARY() != null) {
-      statement.setBoundaryTime(parseDateExpression(ctx.boundaryTime));
+      statement.setBoundaryTime(parseTimeValue(ctx.boundaryTime, DateTimeUtils.currentTime()));
     }
 
     if (ctx.RANGE() != null) {
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FillComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FillComponent.java
index c1b29c1aff..74d3567e49 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FillComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FillComponent.java
@@ -45,4 +45,16 @@ public class FillComponent extends StatementNode {
   public void setFillValue(Literal fillValue) {
     this.fillValue = fillValue;
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("FILL(");
+    if (fillPolicy != FillPolicy.VALUE) {
+      sqlBuilder.append(fillPolicy.toString());
+    } else {
+      sqlBuilder.append(fillValue.toString());
+    }
+    sqlBuilder.append(')');
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FromComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FromComponent.java
index 9c0525c5ec..1bfd1b4df2 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FromComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/FromComponent.java
@@ -39,4 +39,16 @@ public class FromComponent extends StatementNode {
   public List<PartialPath> getPrefixPaths() {
     return prefixPaths;
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("FROM").append(' ');
+    for (int i = 0; i < prefixPaths.size(); i++) {
+      sqlBuilder.append(prefixPaths.get(i).toString());
+      if (i < prefixPaths.size() - 1) {
+        sqlBuilder.append(", ");
+      }
+    }
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByLevelComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByLevelComponent.java
index 4834b14758..4db718f90c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByLevelComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByLevelComponent.java
@@ -51,4 +51,21 @@ public class GroupByLevelComponent extends StatementNode {
   public boolean isCountStar(int i) {
     return isCountStar.get(i);
   }
+
+  public String toSQLString(boolean hasGroupByTime) {
+    StringBuilder sqlBuilder = new StringBuilder();
+    if (hasGroupByTime) {
+      sqlBuilder.append(", ");
+    } else {
+      sqlBuilder.append("GROUP BY ");
+    }
+    sqlBuilder.append("LEVEL = ");
+    for (int i = 0; i < levels.length; i++) {
+      sqlBuilder.append(levels[i]);
+      if (i < levels.length - 1) {
+        sqlBuilder.append(',').append(' ');
+      }
+    }
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByTimeComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByTimeComponent.java
index 9cc1d06341..d17a544fbf 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByTimeComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/GroupByTimeComponent.java
@@ -98,4 +98,39 @@ public class GroupByTimeComponent extends StatementNode {
   public void setIntervalByMonth(boolean isIntervalByMonth) {
     this.isIntervalByMonth = isIntervalByMonth;
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("GROUP BY TIME").append(' ');
+    sqlBuilder.append('(');
+    if (startTime != 0 || endTime != 0) {
+      if (isLeftCRightO()) {
+        sqlBuilder
+            .append('[')
+            .append(startTime)
+            .append(',')
+            .append(' ')
+            .append(endTime)
+            .append(')');
+      } else {
+        sqlBuilder
+            .append('(')
+            .append(startTime)
+            .append(',')
+            .append(' ')
+            .append(endTime)
+            .append(']');
+      }
+      sqlBuilder.append(',').append(' ');
+    }
+    String intervalStr = interval + (isIntervalByMonth ? "mo" : "ms");
+    String slidingStepStr = slidingStep + (isSlidingStepByMonth ? "mo" : "ms");
+    sqlBuilder.append(intervalStr);
+    if (!slidingStepStr.equals(intervalStr)) {
+      sqlBuilder.append(',').append(' ');
+      sqlBuilder.append(slidingStepStr);
+    }
+    sqlBuilder.append(')');
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/HavingCondition.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/HavingCondition.java
index cf76fd1c5c..7bfca1b88a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/HavingCondition.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/HavingCondition.java
@@ -42,4 +42,8 @@ public class HavingCondition extends StatementNode {
   public void setPredicate(Expression predicate) {
     this.predicate = ExpressionAnalyzer.removeAliasFromExpression(predicate);
   }
+
+  public String toSQLString() {
+    return "HAVING " + predicate.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java
index 240a5333e4..30bcab7682 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoComponent.java
@@ -234,4 +234,16 @@ public class IntoComponent extends StatementNode {
       return intoItems.get(deviceIndex).isAligned();
     }
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("INTO ");
+    for (int i = 0; i < intoItems.size(); i++) {
+      sqlBuilder.append(intoItems.get(i).toSQLString());
+      if (i < intoItems.size() - 1) {
+        sqlBuilder.append(", ");
+      }
+    }
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java
index 4efafe3cef..fb4da13874 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/IntoItem.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.db.mpp.plan.statement.StatementNode;
 import java.util.List;
 import java.util.stream.Collectors;
 
+import static org.apache.commons.lang3.StringUtils.SPACE;
 import static org.apache.iotdb.commons.conf.IoTDBConstant.DOUBLE_COLONS;
 import static org.apache.iotdb.commons.conf.IoTDBConstant.LEVELED_PATH_TEMPLATE_PATTERN;
 
@@ -70,4 +71,18 @@ public class IntoItem extends StatementNode {
   public List<PartialPath> getIntoPaths() {
     return intoMeasurements.stream().map(intoDevice::concatNode).collect(Collectors.toList());
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append(intoDevice);
+    sqlBuilder.append('(');
+    for (int i = 0; i < intoMeasurements.size(); i++) {
+      sqlBuilder.append(intoMeasurements.get(i));
+      if (i < intoMeasurements.size() - 1) {
+        sqlBuilder.append(',').append(SPACE);
+      }
+    }
+    sqlBuilder.append(')');
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
index 8b968408a2..3c1d10220a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
@@ -89,4 +89,16 @@ public class OrderByComponent extends StatementNode {
     checkState(timeOrderPriority != -1, "The device order is not specified.");
     return sortItemList.get(deviceOrderPriority).getOrdering();
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("ORDER BY ");
+    for (int i = 0; i < sortItemList.size(); i++) {
+      sqlBuilder.append(sortItemList.get(i).toSQLString());
+      if (i < sortItemList.size() - 1) {
+        sqlBuilder.append(", ");
+      }
+    }
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java
index cc92430f11..475b0a60a3 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SelectComponent.java
@@ -76,7 +76,7 @@ public class SelectComponent extends StatementNode {
     this.aliasToColumnMap = aliasToColumnMap;
   }
 
-  public boolean isHasLast() {
+  public boolean hasLast() {
     return hasLast;
   }
 
@@ -87,4 +87,23 @@ public class SelectComponent extends StatementNode {
   public void setHasBuiltInAggregationFunction(boolean hasBuiltInAggregationFunction) {
     this.hasBuiltInAggregationFunction = hasBuiltInAggregationFunction;
   }
+
+  public String toSQLString() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("SELECT").append(' ');
+    if (hasLast()) {
+      sqlBuilder.append("LAST").append(' ');
+    }
+    for (int i = 0; i < resultColumns.size(); i++) {
+      ResultColumn resultColumn = resultColumns.get(i);
+      sqlBuilder.append(resultColumn.getExpression().toString());
+      if (resultColumn.hasAlias()) {
+        sqlBuilder.append('(').append(resultColumn.getAlias()).append(')');
+      }
+      if (i < resultColumns.size() - 1) {
+        sqlBuilder.append(", ");
+      }
+    }
+    return sqlBuilder.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SortItem.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SortItem.java
index 41fc47dd47..71e169b22d 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SortItem.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SortItem.java
@@ -80,4 +80,8 @@ public class SortItem {
   public int hashCode() {
     return Objects.hash(sortKey, ordering);
   }
+
+  public String toSQLString() {
+    return getSortKey().toString() + " " + getOrdering().toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java
index 9014f345f7..8aa7eb04df 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/WhereCondition.java
@@ -40,4 +40,8 @@ public class WhereCondition extends StatementNode {
   public void setPredicate(Expression predicate) {
     this.predicate = predicate;
   }
+
+  public String toSQLString() {
+    return "WHERE " + predicate.toString();
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
index ab8fd370b1..c36680252c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/crud/QueryStatement.java
@@ -241,7 +241,7 @@ public class QueryStatement extends Statement {
   }
 
   public boolean isLastQuery() {
-    return selectComponent.isHasLast();
+    return selectComponent.hasLast();
   }
 
   public boolean isAggregationQuery() {
@@ -306,6 +306,14 @@ public class QueryStatement extends Statement {
     return orderByComponent.getSortItemList();
   }
 
+  public boolean hasFill() {
+    return fillComponent != null;
+  }
+
+  public boolean hasOrderBy() {
+    return orderByComponent != null;
+  }
+
   public boolean isSelectInto() {
     return intoComponent != null;
   }
@@ -444,6 +452,53 @@ public class QueryStatement extends Statement {
     }
   }
 
+  public String constructFormattedSQL() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append(selectComponent.toSQLString()).append("\n");
+    if (isSelectInto()) {
+      sqlBuilder.append("\t").append(intoComponent.toSQLString()).append("\n");
+    }
+    sqlBuilder.append("\t").append(fromComponent.toSQLString()).append("\n");
+    if (hasWhere()) {
+      sqlBuilder.append("\t").append(whereCondition.toSQLString()).append("\n");
+    }
+    if (isGroupByTime()) {
+      sqlBuilder.append("\t").append(groupByTimeComponent.toSQLString()).append("\n");
+    }
+    if (isGroupByLevel()) {
+      sqlBuilder
+          .append("\t")
+          .append(groupByLevelComponent.toSQLString(isGroupByTime()))
+          .append("\n");
+    }
+    if (hasHaving()) {
+      sqlBuilder.append("\t").append(havingCondition.toSQLString()).append("\n");
+    }
+    if (hasFill()) {
+      sqlBuilder.append("\t").append(fillComponent.toSQLString()).append("\n");
+    }
+    if (hasOrderBy()) {
+      sqlBuilder.append("\t").append(orderByComponent.toSQLString()).append("\n");
+    }
+    if (rowLimit != 0) {
+      sqlBuilder.append("\t").append("LIMIT").append(' ').append(rowLimit).append("\n");
+    }
+    if (rowOffset != 0) {
+      sqlBuilder.append("\t").append("OFFSET").append(' ').append(rowOffset).append("\n");
+    }
+    if (seriesLimit != 0) {
+      sqlBuilder.append("\t").append("SLIMIT").append(' ').append(seriesLimit).append("\n");
+    }
+    if (seriesOffset != 0) {
+      sqlBuilder.append("\t").append("SOFFSET").append(' ').append(seriesOffset).append("\n");
+    }
+    if (isAlignByDevice()) {
+      sqlBuilder.append("\t").append("ALIGN BY DEVICE").append("\n");
+    }
+    sqlBuilder.append(';');
+    return sqlBuilder.toString();
+  }
+
   @Override
   public <R, C> R accept(StatementVisitor<R, C> visitor, C context) {
     return visitor.visitQuery(this, context);
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java
index 8cbe5db20f..1a4a97e9b2 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/BooleanLiteral.java
@@ -88,4 +88,9 @@ public class BooleanLiteral extends Literal {
   public Binary getBinary() {
     return new Binary(String.valueOf(value));
   }
+
+  @Override
+  public String toString() {
+    return String.valueOf(value);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java
index 868d87276a..9b247469e8 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/DoubleLiteral.java
@@ -94,4 +94,9 @@ public class DoubleLiteral extends Literal {
   public Binary getBinary() {
     return new Binary(String.valueOf(value));
   }
+
+  @Override
+  public String toString() {
+    return String.valueOf(value);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java
index c4ce4473b1..182a075a20 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/LongLiteral.java
@@ -117,4 +117,9 @@ public class LongLiteral extends Literal {
   public int hashCode() {
     return Objects.hash(value);
   }
+
+  @Override
+  public String toString() {
+    return String.valueOf(value);
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/NullLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/NullLiteral.java
index 51221a494a..ce2d4e0564 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/NullLiteral.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/NullLiteral.java
@@ -69,4 +69,9 @@ public class NullLiteral extends Literal {
   public int hashCode() {
     return getClass().hashCode();
   }
+
+  @Override
+  public String toString() {
+    return "null";
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java
index 118680ce47..fa220edf3a 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/literal/StringLiteral.java
@@ -82,4 +82,9 @@ public class StringLiteral extends Literal {
   public int hashCode() {
     return Objects.hash(value);
   }
+
+  @Override
+  public String toString() {
+    return String.format("\"%s\"", value.replace("\"", "'"));
+  }
 }
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateContinuousQueryStatement.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateContinuousQueryStatement.java
index 641a9365ba..9aee94b8a3 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateContinuousQueryStatement.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/metadata/CreateContinuousQueryStatement.java
@@ -37,7 +37,6 @@ import java.util.List;
 
 public class CreateContinuousQueryStatement extends Statement implements IConfigStatement {
 
-  private String sql;
   private String cqId;
 
   // The query execution time interval, default value is group_by_interval in group by clause.
@@ -56,22 +55,14 @@ public class CreateContinuousQueryStatement extends Statement implements IConfig
   // while the next execution time has reached, default value is BLOCKED.
   private TimeoutPolicy timeoutPolicy = TimeoutPolicy.BLOCKED;
 
-  private String queryBody;
   private QueryStatement queryBodyStatement;
+  private String queryBody;
 
   public CreateContinuousQueryStatement() {
     super();
     statementType = StatementType.CREATE_CONTINUOUS_QUERY;
   }
 
-  public String getSql() {
-    return sql;
-  }
-
-  public void setSql(String sql) {
-    this.sql = sql;
-  }
-
   public String getCqId() {
     return cqId;
   }
@@ -120,14 +111,6 @@ public class CreateContinuousQueryStatement extends Statement implements IConfig
     this.timeoutPolicy = timeoutPolicy;
   }
 
-  public String getQueryBody() {
-    return queryBody;
-  }
-
-  public void setQueryBody(String queryBody) {
-    this.queryBody = queryBody;
-  }
-
   public QueryStatement getQueryBodyStatement() {
     return queryBodyStatement;
   }
@@ -140,6 +123,41 @@ public class CreateContinuousQueryStatement extends Statement implements IConfig
     return queryBodyStatement.getSelectComponent().getZoneId().getId();
   }
 
+  public String getSql() {
+    return constructFormattedSQL();
+  }
+
+  public String getQueryBody() {
+    if (queryBody == null) {
+      queryBody = queryBodyStatement.constructFormattedSQL();
+    }
+    return queryBody;
+  }
+
+  public String constructFormattedSQL() {
+    StringBuilder sqlBuilder = new StringBuilder();
+    sqlBuilder.append("CREATE CQ ").append(cqId).append('\n');
+    sqlBuilder.append("RESAMPLE\n");
+    sqlBuilder.append('\t').append("EVERY ").append(everyInterval).append("ms\n");
+    sqlBuilder.append('\t').append("BOUNDARY ").append(boundaryTime).append("\n");
+    ;
+    sqlBuilder.append('\t').append("RANGE ").append(startTimeOffset).append("ms");
+    if (endTimeOffset != 0) {
+      sqlBuilder.append(", ").append(endTimeOffset).append("ms\n");
+    } else {
+      sqlBuilder.append("\n");
+    }
+    sqlBuilder.append("TIMEOUT POLICY ").append(timeoutPolicy.toString()).append('\n');
+    sqlBuilder.append("BEGIN\n");
+    String[] queryBodySlices = getQueryBody().split("\n");
+    for (int i = 0; i < queryBodySlices.length - 1; i++) { // skip ';' in queryBody
+      sqlBuilder.append('\t').append(queryBodySlices[i]).append('\n');
+    }
+    sqlBuilder.append("END\n");
+    sqlBuilder.append(";");
+    return sqlBuilder.toString();
+  }
+
   @Override
   public QueryType getQueryType() {
     return QueryType.WRITE;
@@ -159,7 +177,10 @@ public class CreateContinuousQueryStatement extends Statement implements IConfig
     if (everyInterval
         < IoTDBDescriptor.getInstance().getConfig().getContinuousQueryMinimumEveryInterval()) {
       throw new SemanticException(
-          "CQ: Every interval should not be lower than the `continuous_query_minimum_every_interval` configured.");
+          String.format(
+              "CQ: Every interval [%d] should not be lower than the `continuous_query_minimum_every_interval` [%d] configured.",
+              everyInterval,
+              IoTDBDescriptor.getInstance().getConfig().getContinuousQueryMinimumEveryInterval()));
     }
     if (startTimeOffset <= 0) {
       throw new SemanticException("CQ: The start time offset should be greater than 0.");
@@ -177,15 +198,17 @@ public class CreateContinuousQueryStatement extends Statement implements IConfig
     }
 
     if (!queryBodyStatement.isSelectInto()) {
-      throw new SemanticException("CQ: The query body is missing an INTO clause.");
+      throw new SemanticException("CQ: The query body misses an INTO clause.");
     }
     GroupByTimeComponent groupByTimeComponent = queryBodyStatement.getGroupByTimeComponent();
-    if (groupByTimeComponent.getStartTime() != 0 || groupByTimeComponent.getEndTime() != 0) {
+    if (groupByTimeComponent != null
+        && (groupByTimeComponent.getStartTime() != 0 || groupByTimeComponent.getEndTime() != 0)) {
       throw new SemanticException(
-          "CQ: Specifying time range in GROUP BY TIME clause is prohibited");
+          "CQ: Specifying time range in GROUP BY TIME clause is prohibited.");
     }
-    if (ExpressionAnalyzer.checkIfTimeFilterExist(
-        queryBodyStatement.getWhereCondition().getPredicate())) {
+    if (queryBodyStatement.getWhereCondition() != null
+        && ExpressionAnalyzer.checkIfTimeFilterExist(
+            queryBodyStatement.getWhereCondition().getPredicate())) {
       throw new SemanticException("CQ: Specifying time filters in the query body is prohibited.");
     }
   }
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 7c3aafec55..108ea6fd8b 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
@@ -816,7 +816,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
     }
 
     if (ctx.BOUNDARY() != null) {
-      operator.setFirstExecutionTimeBoundary(parseDateExpression(ctx.dateExpression()));
+      operator.setFirstExecutionTimeBoundary(
+          parseTimeValue(ctx.timeValue(), DateTimeUtils.currentTime()));
     }
   }
 
diff --git a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
index 55afe02880..b9e9b90ec1 100644
--- a/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++ b/server/src/main/java/org/apache/iotdb/db/service/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -752,6 +752,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface
 
     long sessionId =
         SESSION_MANAGER.requestSessionId(req.cqId, req.zoneId, IoTDBConstant.ClientVersion.V_0_13);
+    String executedSQL = req.queryBody;
 
     try {
       QueryStatement s =
@@ -781,6 +782,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface
         s.getGroupByTimeComponent().setStartTime(req.startTime);
         s.getGroupByTimeComponent().setEndTime(req.endTime);
       }
+      executedSQL = String.join(" ", s.constructFormattedSQL().split("\n")).replaceAll(" +", " ");
 
       QUERY_FREQUENCY_RECORDER.incrementAndGet();
 
@@ -792,7 +794,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface
               s,
               queryId,
               SESSION_MANAGER.getSessionInfo(sessionId),
-              req.queryBody,
+              executedSQL,
               PARTITION_FETCHER,
               SCHEMA_FETCHER,
               req.getTimeout());
@@ -818,7 +820,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface
       }
     } catch (Exception e) {
       // TODO call the coordinator to release query resource
-      return onQueryException(e, "\"" + req.queryBody + "\". " + OperationType.EXECUTE_STATEMENT);
+      return onQueryException(e, "\"" + executedSQL + "\". " + OperationType.EXECUTE_STATEMENT);
     } finally {
       SESSION_MANAGER.releaseSessionResource(sessionId, this::cleanupQueryExecution);
       SESSION_MANAGER.closeSession(sessionId);