You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ja...@apache.org on 2023/06/29 12:36:48 UTC

[iotdb] branch rel/1.1 updated: [To rel/1.1] Fix error sql when using raw query and align by device with having clause

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

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


The following commit(s) were added to refs/heads/rel/1.1 by this push:
     new f1fb782e76c [To rel/1.1] Fix error sql when using raw query and align by device with having clause
f1fb782e76c is described below

commit f1fb782e76cad10516d047cf487cf5e7455a5c3c
Author: Beyyes <cg...@foxmail.com>
AuthorDate: Thu Jun 29 20:36:41 2023 +0800

    [To rel/1.1] Fix error sql when using raw query and align by device with having clause
---
 .../db/mpp/plan/statement/crud/QueryStatement.java |  14 +-
 .../db/mpp/plan/statement/QueryStatementTest.java  | 148 +++++++++++++++++++++
 2 files changed, 161 insertions(+), 1 deletion(-)

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 7ffb1b7299f..9e06d677962 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
@@ -465,8 +465,13 @@ public class QueryStatement extends Statement {
           != ResultColumn.ColumnType.AGGREGATION) {
         throw new SemanticException("Expression of HAVING clause must to be an Aggregation");
       }
+      if (!isAggregationQuery()) {
+        throw new SemanticException(
+            "Expression of HAVING clause can not be used in NonAggregationQuery");
+      }
       try {
-        if (isGroupByLevel()) { // check path in SELECT and HAVING only have one node
+        if (isGroupByLevel()) {
+          // check path in SELECT and HAVING only have one node
           for (ResultColumn resultColumn : getSelectComponent().getResultColumns()) {
             ExpressionAnalyzer.checkIsAllMeasurement(resultColumn.getExpression());
           }
@@ -483,9 +488,16 @@ public class QueryStatement extends Statement {
         for (ResultColumn resultColumn : selectComponent.getResultColumns()) {
           ExpressionAnalyzer.checkIsAllMeasurement(resultColumn.getExpression());
         }
+        if (hasGroupByExpression()) {
+          ExpressionAnalyzer.checkIsAllMeasurement(
+              getGroupByComponent().getControlColumnExpression());
+        }
         if (getWhereCondition() != null) {
           ExpressionAnalyzer.checkIsAllMeasurement(getWhereCondition().getPredicate());
         }
+        if (hasHaving()) {
+          ExpressionAnalyzer.checkIsAllMeasurement(getHavingCondition().getPredicate());
+        }
       } catch (SemanticException e) {
         throw new SemanticException("ALIGN BY DEVICE: " + e.getMessage());
       }
diff --git a/server/src/test/java/org/apache/iotdb/db/mpp/plan/statement/QueryStatementTest.java b/server/src/test/java/org/apache/iotdb/db/mpp/plan/statement/QueryStatementTest.java
new file mode 100644
index 00000000000..acc2d45d09f
--- /dev/null
+++ b/server/src/test/java/org/apache/iotdb/db/mpp/plan/statement/QueryStatementTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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.mpp.plan.statement;
+
+import org.apache.iotdb.db.exception.sql.SemanticException;
+import org.apache.iotdb.db.mpp.plan.parser.StatementGenerator;
+import org.apache.iotdb.db.mpp.plan.statement.crud.QueryStatement;
+import org.apache.iotdb.tsfile.utils.Pair;
+
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.time.ZonedDateTime;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class QueryStatementTest {
+
+  private static final Logger logger = LoggerFactory.getLogger(QueryStatementTest.class);
+
+  private static final String ALIGN_BY_DEVICE_ONE_LEVEL_ERROR =
+      "ALIGN BY DEVICE: the suffix paths can only be measurement or one-level wildcard";
+
+  @Test
+  public void semanticCheckTest() {
+    List<Pair<String, String>> errorSqlWithMessages =
+        Arrays.asList(
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 "
+                    + "GROUP BY ([2017-11-01T00:00:00, 2017-11-07T23:00:00),1d)",
+                "Common queries and aggregated queries are not allowed to appear at the same time"),
+            new Pair<>(
+                "SELECT count(s1),s2 FROM root.sg.d1",
+                "Raw data and aggregation hybrid query is not supported."),
+
+            // test for where clause
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 WHERE count(s1) > 0",
+                "aggregate functions are not supported in WHERE clause"),
+
+            // test for having clause
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 HAVING(s1 > 0)",
+                "Expression of HAVING clause must to be an Aggregation"),
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 HAVING(count(s1) > 0)",
+                "Expression of HAVING clause can not be used in NonAggregationQuery"),
+            new Pair<>(
+                "SELECT count(d1.s1) FROM root.sg.d1 GROUP BY level=1 HAVING (count(s1) > 0)",
+                "When Having used with GroupByLevel: "
+                    + "the suffix paths can only be measurement or one-level wildcard"),
+            new Pair<>(
+                "SELECT count(s1) FROM root.sg.d1 GROUP BY level=1 HAVING (count(sg.d1.s1) > 0)",
+                "When Having used with GroupByLevel: "
+                    + "the suffix paths can only be measurement or one-level wildcard"),
+
+            // test for align by device clause
+            new Pair<>(
+                "SELECT d1.s1 FROM root.sg.d1 align by device", ALIGN_BY_DEVICE_ONE_LEVEL_ERROR),
+            new Pair<>(
+                "SELECT count(s1) FROM root.sg.d1 group by variation(sg.s1) align by device",
+                ALIGN_BY_DEVICE_ONE_LEVEL_ERROR),
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 where root.sg.d1.s1 > 0 align by device",
+                ALIGN_BY_DEVICE_ONE_LEVEL_ERROR),
+            new Pair<>(
+                "SELECT count(s1) FROM root.sg.d1 having(count(root.sg.d1.s1) > 0) align by device",
+                ALIGN_BY_DEVICE_ONE_LEVEL_ERROR),
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 order by timeseries align by device",
+                "Sorting by timeseries is only supported in last queries."),
+
+            // test for last query
+            new Pair<>(
+                "SELECT last s1 FROM root.sg.d1 align by device",
+                "Last query doesn't support align by device."),
+            new Pair<>(
+                "SELECT last s1+s2 FROM root.sg.d1",
+                "Last queries can only be applied on raw time series."),
+            new Pair<>(
+                "SELECT last s1 FROM root.sg.d1 order by device",
+                "Sorting by device is only supported in ALIGN BY DEVICE queries."),
+            new Pair<>(
+                "SELECT last s1 FROM root.sg.d1 SLIMIT 1 SOFFSET 2",
+                "SLIMIT and SOFFSET can not be used in LastQuery."),
+
+            // test for select into clause
+            new Pair<>(
+                "SELECT s1 INTO root.sg.d2(t1) FROM root.sg.d1 SLIMIT 5",
+                "select into: slimit clauses are not supported."),
+            new Pair<>(
+                "SELECT s1 INTO root.sg.d2(t1) FROM root.sg.d1 SOFFSET 6",
+                "select into: soffset clauses are not supported."),
+            new Pair<>(
+                "SELECT last s1 INTO root.sg.d2(t1) FROM root.sg.d1",
+                "select into: last clauses are not supported."),
+            new Pair<>(
+                "SELECT count(s1) INTO root.sg.d2(t1) FROM root.sg.d1 GROUP BY TAGS(a)",
+                "select into: GROUP BY TAGS clause are not supported."),
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 order by timeseries",
+                "Sorting by timeseries is only supported in last queries."),
+            new Pair<>(
+                "SELECT s1 FROM root.sg.d1 order by device",
+                "Sorting by device is only supported in ALIGN BY DEVICE queries."));
+
+    for (Pair<String, String> pair : errorSqlWithMessages) {
+      String errorSql = pair.left;
+      String errorMsg = pair.right;
+      try {
+        checkErrorQuerySql(errorSql);
+      } catch (SemanticException e) {
+        assertEquals(errorMsg, e.getMessage());
+        continue;
+      } catch (Exception ex) {
+        fail(String.format("Meets exception %s in test sql: `%s`", errorMsg, errorSql));
+      }
+      fail(String.format("Sql: `%s` must throw exception: %s", errorSql, errorMsg));
+    }
+  }
+
+  private void checkErrorQuerySql(String sql) {
+    QueryStatement statement =
+        (QueryStatement) StatementGenerator.createStatement(sql, ZonedDateTime.now().getOffset());
+    statement.semanticCheck();
+  }
+}