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/07/20 03:18:51 UTC

[iotdb] 01/04: refactor Order by in SQL parser & QueryStatement

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

hui pushed a commit to branch lmh/orderBySensor
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit c53a1d49ed29dfe73ff725152f2dc3e75fc2857d
Author: Minghui Liu <li...@foxmail.com>
AuthorDate: Tue Jul 19 17:51:01 2022 +0800

    refactor Order by in SQL parser & QueryStatement
---
 .../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4   | 24 +++++--
 .../iotdb/db/mpp/plan/parser/ASTVisitor.java       | 47 ++++++++----
 .../{OrderBy.java => OrderByComponent.java}        | 32 +++++----
 .../db/mpp/plan/statement/component/SortItem.java  | 83 ++++++++++++++++++++++
 .../db/mpp/plan/statement/crud/QueryStatement.java | 12 ++--
 .../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java    | 36 +++++++---
 6 files changed, 182 insertions(+), 52 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 fe0add2da7..03868d644a 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
@@ -379,11 +379,11 @@ intoPath
 
 specialClause
     : specialLimit #specialLimitStatement
-    | orderByTimeClause specialLimit? #orderByTimeStatement
-    | groupByTimeClause orderByTimeClause? specialLimit? #groupByTimeStatement
-    | groupByFillClause orderByTimeClause? specialLimit? #groupByFillStatement
-    | groupByLevelClause orderByTimeClause? specialLimit? #groupByLevelStatement
-    | fillClause orderByTimeClause? specialLimit? #fillStatement
+    | orderByClause specialLimit? #orderByTimeStatement
+    | groupByTimeClause orderByClause? specialLimit? #groupByTimeStatement
+    | groupByFillClause orderByClause? specialLimit? #groupByFillStatement
+    | groupByLevelClause orderByClause? specialLimit? #groupByLevelStatement
+    | fillClause orderByClause? specialLimit? #fillStatement
     ;
 
 specialLimit
@@ -407,8 +407,18 @@ disableAlign
     : DISABLE ALIGN
     ;
 
-orderByTimeClause
-    : ORDER BY TIME (DESC | ASC)?
+orderByClause
+    : ORDER BY orderByAttributeClause (COMMA orderByAttributeClause)*
+    ;
+
+orderByAttributeClause
+    : sortKey (DESC | ASC)
+    ;
+
+sortKey
+    : TIME
+    | TIMESERIES
+    | DEVICE
     ;
 
 groupByTimeClause
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 7c8f642936..8d5f002540 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
@@ -64,10 +64,11 @@ import org.apache.iotdb.db.mpp.plan.statement.component.FillPolicy;
 import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByLevelComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent;
-import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy;
+import org.apache.iotdb.db.mpp.plan.statement.component.OrderByComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultSetFormat;
 import org.apache.iotdb.db.mpp.plan.statement.component.SelectComponent;
+import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
 import org.apache.iotdb.db.mpp.plan.statement.component.WhereCondition;
 import org.apache.iotdb.db.mpp.plan.statement.crud.DeleteDataStatement;
 import org.apache.iotdb.db.mpp.plan.statement.crud.InsertStatement;
@@ -142,9 +143,11 @@ import java.net.URISyntaxException;
 import java.time.ZoneId;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.regex.Pattern;
 
 /** Parse AST to Statement. */
@@ -790,8 +793,8 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
     parseGroupByTimeClause(ctx.groupByTimeClause());
 
     // parse order by time
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
 
     // parse limit & offset
@@ -808,8 +811,8 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
     parseGroupByTimeClause(ctx.groupByFillClause());
 
     // parse order by time
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
 
     // parse limit & offset
@@ -938,8 +941,8 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
     parseGroupByLevelClause(ctx.groupByLevelClause());
 
     // parse order by time
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
 
     // parse limit & offset
@@ -972,8 +975,8 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
     parseFillClause(ctx.fillClause());
 
     // parse order by time
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
 
     // parse limit & offset
@@ -1182,7 +1185,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
   @Override
   public Statement visitOrderByTimeStatement(IoTDBSqlParser.OrderByTimeStatementContext ctx) {
     // parse ORDER BY TIME
-    parseOrderByTimeClause(ctx.orderByTimeClause());
+    parseOrderByClause(ctx.orderByClause());
 
     // parse others
     if (ctx.specialLimit() != null) {
@@ -1192,10 +1195,28 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
   }
 
   // parse ORDER BY TIME
-  private void parseOrderByTimeClause(IoTDBSqlParser.OrderByTimeClauseContext ctx) {
-    if (ctx.DESC() != null) {
-      queryStatement.setResultOrder(OrderBy.TIMESTAMP_DESC);
+  private void parseOrderByClause(IoTDBSqlParser.OrderByClauseContext ctx) {
+    OrderByComponent orderByComponent = new OrderByComponent();
+    Set<SortItem.SortKey> sortKeySet = new HashSet<>();
+    for (IoTDBSqlParser.OrderByAttributeClauseContext orderByAttributeClauseContext :
+        ctx.orderByAttributeClause()) {
+      SortItem sortItem = parseOrderByAttributeClause(orderByAttributeClauseContext);
+
+      SortItem.SortKey sortKey = sortItem.getSortKey();
+      if (sortKeySet.contains(sortKey)) {
+        throw new SemanticException(String.format("ORDER BY: duplicate sort key '%s'", sortKey));
+      } else {
+        sortKeySet.add(sortKey);
+        orderByComponent.addSortItem(sortItem);
+      }
     }
+    queryStatement.setOrderByComponent(orderByComponent);
+  }
+
+  private SortItem parseOrderByAttributeClause(IoTDBSqlParser.OrderByAttributeClauseContext ctx) {
+    return new SortItem(
+        SortItem.SortKey.valueOf(ctx.sortKey().getText().toUpperCase()),
+        ctx.DESC() != null ? SortItem.Ordering.DESC : SortItem.Ordering.ASC);
   }
 
   // ResultSetFormat Clause
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderBy.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
similarity index 68%
rename from server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderBy.java
rename to server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
index e72029b7d4..1aa09bbcae 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderBy.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/OrderByComponent.java
@@ -19,21 +19,23 @@
 
 package org.apache.iotdb.db.mpp.plan.statement.component;
 
-/** The order of query result set by timestamp */
-public enum OrderBy {
-  TIMESTAMP_ASC,
-  TIMESTAMP_DESC,
-  DEVICE_ASC,
-  DEVICE_DESC;
+import java.util.ArrayList;
+import java.util.List;
 
-  public OrderBy reverse() {
-    switch (this) {
-      case TIMESTAMP_ASC:
-        return TIMESTAMP_DESC;
-      case TIMESTAMP_DESC:
-        return TIMESTAMP_ASC;
-      default:
-        throw new UnsupportedOperationException();
-    }
+/** The order of query result set */
+public class OrderByComponent {
+
+  private final List<SortItem> sortItemList;
+
+  public OrderByComponent() {
+    this.sortItemList = new ArrayList<>();
+  }
+
+  public void addSortItem(SortItem sortItem) {
+    this.sortItemList.add(sortItem);
+  }
+
+  public List<SortItem> getSortItemList() {
+    return sortItemList;
   }
 }
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
new file mode 100644
index 0000000000..bbdff9f01b
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/statement/component/SortItem.java
@@ -0,0 +1,83 @@
+/*
+ * 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.component;
+
+import org.apache.iotdb.db.mpp.plan.statement.StatementNode;
+
+import java.util.Objects;
+
+public class SortItem extends StatementNode {
+
+  public enum SortKey {
+    TIME,
+    TIMESERIES,
+    DEVICE
+  }
+
+  public enum Ordering {
+    ASC,
+    DESC;
+
+    public Ordering reverse() {
+      if (this == ASC) {
+        return DESC;
+      } else {
+        return ASC;
+      }
+    }
+  }
+
+  private final SortKey sortKey;
+  private final Ordering ordering;
+
+  public SortItem(SortKey sortKey, Ordering ordering) {
+    this.sortKey = sortKey;
+    this.ordering = ordering;
+  }
+
+  public SortKey getSortKey() {
+    return sortKey;
+  }
+
+  public Ordering getOrdering() {
+    return ordering;
+  }
+
+  public SortItem reverse() {
+    return new SortItem(getSortKey(), getOrdering().reverse());
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    SortItem sortItem = (SortItem) o;
+    return sortKey == sortItem.sortKey && ordering == sortItem.ordering;
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(sortKey, ordering);
+  }
+}
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 17f433a2e7..2da55d6f42 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
@@ -31,7 +31,7 @@ import org.apache.iotdb.db.mpp.plan.statement.component.FillComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.FromComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByLevelComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.GroupByTimeComponent;
-import org.apache.iotdb.db.mpp.plan.statement.component.OrderBy;
+import org.apache.iotdb.db.mpp.plan.statement.component.OrderByComponent;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultColumn;
 import org.apache.iotdb.db.mpp.plan.statement.component.ResultSetFormat;
 import org.apache.iotdb.db.mpp.plan.statement.component.SelectComponent;
@@ -76,7 +76,7 @@ public class QueryStatement extends Statement {
 
   protected FillComponent fillComponent;
 
-  protected OrderBy resultOrder = OrderBy.TIMESTAMP_ASC;
+  protected OrderByComponent orderByComponent;
 
   protected ResultSetFormat resultSetFormat = ResultSetFormat.ALIGN_BY_TIME;
 
@@ -159,12 +159,12 @@ public class QueryStatement extends Statement {
     this.fillComponent = fillComponent;
   }
 
-  public OrderBy getResultOrder() {
-    return resultOrder;
+  public OrderByComponent getOrderByComponent() {
+    return orderByComponent;
   }
 
-  public void setResultOrder(OrderBy resultOrder) {
-    this.resultOrder = resultOrder;
+  public void setOrderByComponent(OrderByComponent orderByComponent) {
+    this.orderByComponent = orderByComponent;
   }
 
   public ResultSetFormat getResultSetFormat() {
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 79bb5d9db0..d4c58d998c 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
@@ -53,6 +53,7 @@ import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
 import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression;
 import org.apache.iotdb.db.mpp.plan.expression.unary.NegationExpression;
 import org.apache.iotdb.db.mpp.plan.expression.unary.RegularExpression;
+import org.apache.iotdb.db.mpp.plan.statement.component.SortItem;
 import org.apache.iotdb.db.qp.constant.FilterConstant;
 import org.apache.iotdb.db.qp.constant.FilterConstant.FilterType;
 import org.apache.iotdb.db.qp.constant.SQLConstant;
@@ -1341,7 +1342,7 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   @Override
   public Operator visitOrderByTimeStatement(IoTDBSqlParser.OrderByTimeStatementContext ctx) {
     queryOp = new QueryOperator();
-    parseOrderByTimeClause(ctx.orderByTimeClause());
+    parseOrderByClause(ctx.orderByClause());
     if (ctx.specialLimit() != null) {
       return visit(ctx.specialLimit());
     }
@@ -1352,8 +1353,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   public Operator visitGroupByTimeStatement(IoTDBSqlParser.GroupByTimeStatementContext ctx) {
     queryOp = new GroupByQueryOperator();
     parseGroupByTimeClause(ctx.groupByTimeClause());
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
     if (ctx.specialLimit() != null) {
       return visit(ctx.specialLimit());
@@ -1365,8 +1366,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   public Operator visitGroupByFillStatement(IoTDBSqlParser.GroupByFillStatementContext ctx) {
     queryOp = new GroupByFillQueryOperator();
     parseGroupByFillClause(ctx.groupByFillClause());
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
     if (ctx.specialLimit() != null) {
       return visit(ctx.specialLimit());
@@ -1378,8 +1379,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   public Operator visitGroupByLevelStatement(IoTDBSqlParser.GroupByLevelStatementContext ctx) {
     queryOp = new AggregationQueryOperator();
     parseGroupByLevelClause(ctx.groupByLevelClause());
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
     if (ctx.specialLimit() != null) {
       return visit(ctx.specialLimit());
@@ -1391,8 +1392,8 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
   public Operator visitFillStatement(IoTDBSqlParser.FillStatementContext ctx) {
     queryOp = new FillQueryOperator();
     parseFillClause(ctx.fillClause());
-    if (ctx.orderByTimeClause() != null) {
-      parseOrderByTimeClause(ctx.orderByTimeClause());
+    if (ctx.orderByClause() != null) {
+      parseOrderByClause(ctx.orderByClause());
     }
     if (ctx.specialLimit() != null) {
       return visit(ctx.specialLimit());
@@ -1480,8 +1481,15 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
     specialClauseComponent.setAlignByTime(false);
   }
 
-  private void parseOrderByTimeClause(IoTDBSqlParser.OrderByTimeClauseContext ctx) {
-    if (ctx.DESC() != null) {
+  private void parseOrderByClause(IoTDBSqlParser.OrderByClauseContext ctx) {
+    if (ctx.orderByAttributeClause().size() > 0) {
+      throw new SQLParserException("Sorting by multiple fields is not supported.");
+    }
+    SortItem sortItem = parseOrderByAttributeClause(ctx.orderByAttributeClause(0));
+    if (sortItem.getSortKey() != SortItem.SortKey.TIME) {
+      throw new SQLParserException("It only supports sorting by time.");
+    }
+    if (sortItem.getOrdering() == SortItem.Ordering.DESC) {
       SpecialClauseComponent specialClauseComponent = queryOp.getSpecialClauseComponent();
       if (specialClauseComponent == null) {
         specialClauseComponent = new SpecialClauseComponent();
@@ -1491,6 +1499,12 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
     }
   }
 
+  private SortItem parseOrderByAttributeClause(IoTDBSqlParser.OrderByAttributeClauseContext ctx) {
+    return new SortItem(
+        SortItem.SortKey.valueOf(ctx.sortKey().getText().toUpperCase()),
+        ctx.DESC() != null ? SortItem.Ordering.DESC : SortItem.Ordering.ASC);
+  }
+
   private void parseGroupByTimeClause(IoTDBSqlParser.GroupByTimeClauseContext ctx) {
     GroupByClauseComponent groupByClauseComponent = new GroupByClauseComponent();
     groupByClauseComponent.setLeftCRightO(ctx.timeRange().LS_BRACKET() != null);