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/03/07 01:53:18 UTC
[iotdb] branch master updated: [IOTDB-5631] Add aggregate method time_duration for query
This is an automated email from the ASF dual-hosted git repository.
jackietien pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 91414196aa [IOTDB-5631] Add aggregate method time_duration for query
91414196aa is described below
commit 91414196aa6a435a83a3b298f7bd2b91ba5c3738
Author: Zhijia Cao <ca...@126.com>
AuthorDate: Tue Mar 7 09:53:12 2023 +0800
[IOTDB-5631] Add aggregate method time_duration for query
---
docs/UserGuide/Operators-Functions/Aggregation.md | 80 ++++-
.../UserGuide/Operators-Functions/Aggregation.md | 80 ++++-
.../apache/iotdb/itbase/constant/TestConstant.java | 4 +
.../it/aggregation/IoTDBAggregationOptimizeIT.java | 8 +-
.../db/it/aggregation/IoTDBTimeDurationIT.java | 368 +++++++++++++++++++++
.../udf/builtin/BuiltinAggregationFunction.java | 4 +-
.../org/apache/iotdb/db/constant/SqlConstant.java | 1 +
.../db/mpp/aggregation/AccumulatorFactory.java | 2 +
.../mpp/aggregation/TimeDurationAccumulator.java | 124 +++++++
.../SlidingWindowAggregatorFactory.java | 2 +
.../iotdb/db/mpp/plan/parser/ASTVisitor.java | 1 +
.../plan/parameter/AggregationDescriptor.java | 4 +
.../org/apache/iotdb/db/utils/SchemaUtils.java | 4 +
.../apache/iotdb/db/utils/TypeInferenceUtils.java | 3 +
thrift-commons/src/main/thrift/common.thrift | 3 +-
15 files changed, 657 insertions(+), 31 deletions(-)
diff --git a/docs/UserGuide/Operators-Functions/Aggregation.md b/docs/UserGuide/Operators-Functions/Aggregation.md
index c20947908e..0be24a85ec 100644
--- a/docs/UserGuide/Operators-Functions/Aggregation.md
+++ b/docs/UserGuide/Operators-Functions/Aggregation.md
@@ -27,19 +27,20 @@ All aggregate functions except `COUNT()`, `COUNT_IF()` ignore null values and re
The aggregate functions supported by IoTDB are as follows:
-| Function Name | Function Description | Allowed Input Data Types | Output Data Types [...]
-| ------------- |------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...]
-| SUM | Summation. | INT32 INT64 FLOAT DOUBLE | DOUBLE [...]
-| COUNT | Counts the number of data points. | All types | INT [...]
-| AVG | Average. | INT32 INT64 FLOAT DOUBLE | DOUBLE [...]
-| EXTREME | Finds the value with the largest absolute value. Returns a positive value if the maximum absolute value of positive and negative values is equal. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
-| MAX_VALUE | Find the maximum value. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
-| MIN_VALUE | Find the minimum value. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
-| FIRST_VALUE | Find the value with the smallest timestamp. | All data types | Consistent with input data type [...]
-| LAST_VALUE | Find the value with the largest timestamp. | All data types | Consistent with input data type [...]
-| MAX_TIME | Find the maximum timestamp. | All data Types | Timestamp [...]
-| MIN_TIME | Find the minimum timestamp. | All data Types | Timestamp [...]
-| COUNT_IF | Find the number of data points that continuously meet a given condition and the number of data points that meet the condition (represented by keep) meet the specified threshold. | BOOLEAN | `[keep >=/>/=/!=/</<=]threshold`:The specified threshold or threshold condition, it is equivalent to `keep >= threshold` if `threshold` is used alone, type of `threshold` is `INT64`<br/> `ignoreNull`:Optional, default value is `true`;If the value is `true`, null values [...]
+| Function Name | Function Description | Allowed Input Data Types | Output Data Types [...]
+|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------| ------------------------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- [...]
+| SUM | Summation. | INT32 INT64 FLOAT DOUBLE | DOUBLE [...]
+| COUNT | Counts the number of data points. | All types | INT [...]
+| AVG | Average. | INT32 INT64 FLOAT DOUBLE | DOUBLE [...]
+| EXTREME | Finds the value with the largest absolute value. Returns a positive value if the maximum absolute value of positive and negative values is equal. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
+| MAX_VALUE | Find the maximum value. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
+| MIN_VALUE | Find the minimum value. | INT32 INT64 FLOAT DOUBLE | Consistent with the input data type [...]
+| FIRST_VALUE | Find the value with the smallest timestamp. | All data types | Consistent with input data type [...]
+| LAST_VALUE | Find the value with the largest timestamp. | All data types | Consistent with input data type [...]
+| MAX_TIME | Find the maximum timestamp. | All data Types | Timestamp [...]
+| MIN_TIME | Find the minimum timestamp. | All data Types | Timestamp [...]
+| COUNT_IF | Find the number of data points that continuously meet a given condition and the number of data points that meet the condition (represented by keep) meet the specified threshold. | BOOLEAN | `[keep >=/>/=/!=/</<=]threshold`:The specified threshold or threshold condition, it is equivalent to `keep >= threshold` if `threshold` is used alone, type of `threshold` is `INT64`<br/> `ignoreNull`:Optional, default value is `true`;If the value is `true`, null valu [...]
+| TIME_DURATION | Find the difference between the timestamp of the largest non-null value and the timestamp of the smallest non-null value in a column | All data Types | INT64 [...]
## COUNT
@@ -123,4 +124,57 @@ Result:
+------------------------------------------------------------------------+------------------------------------------------------------------------+
| 1| 1|
+------------------------------------------------------------------------+------------------------------------------------------------------------+
+```
+
+## TIME_DURATION
+### Grammar
+```sql
+ time_duration(Path)
+```
+### Example
+#### raw data
+```sql
++----------+-------------+
+| Time|root.db.d1.s1|
++----------+-------------+
+| 1| 70|
+| 3| 10|
+| 4| 303|
+| 6| 110|
+| 7| 302|
+| 8| 110|
+| 9| 60|
+| 10| 70|
+|1677570934| 30|
++----------+-------------+
+```
+#### Insert sql
+```sql
+"CREATE DATABASE root.db",
+"CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+```
+
+SQL:
+```sql
+select time_duration(s1) from root.db.d1.s1
+```
+
+Result:
+```
++----------------------------+
+|time_duration(root.db.d1.s1)|
++----------------------------+
+| 1677570933|
++----------------------------+
```
\ No newline at end of file
diff --git a/docs/zh/UserGuide/Operators-Functions/Aggregation.md b/docs/zh/UserGuide/Operators-Functions/Aggregation.md
index 0e63b6a0d8..2945d4e453 100644
--- a/docs/zh/UserGuide/Operators-Functions/Aggregation.md
+++ b/docs/zh/UserGuide/Operators-Functions/Aggregation.md
@@ -27,19 +27,20 @@
IoTDB 支持的聚合函数如下:
-| 函数名 | 功能描述 | 允许的输入类型 | 必要的属性参数 | 输出类型 |
-| ----------- |-----------------------------------------------| ------------------------ |----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
-| SUM | 求和。 | INT32 INT64 FLOAT DOUBLE | 无 | DOUBLE |
-| COUNT | 计算数据点数。 | 所有类型 | 无 | INT64 |
-| AVG | 求平均值。 | INT32 INT64 FLOAT DOUBLE | 无 | DOUBLE |
-| EXTREME | 求具有最大绝对值的值。如果正值和负值的最大绝对值相等,则返回正值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
-| MAX_VALUE | 求最大值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
-| MIN_VALUE | 求最小值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
-| FIRST_VALUE | 求时间戳最小的值。 | 所有类型 | 无 | 与输入类型一致 |
-| LAST_VALUE | 求时间戳最大的值。 | 所有类型 | 无 | 与输入类型一致 |
-| MAX_TIME | 求最大时间戳。 | 所有类型 | 无 | Timestamp |
-| MIN_TIME | 求最小时间戳。 | 所有类型 | 无 | Timestamp |
-| COUNT_IF | 求数据点连续满足某一给定条件,且满足条件的数据点个数(用keep表示)满足指定阈值的次数。 | BOOLEAN | `[keep >=/>/=/!=/</<=]threshold`:被指定的阈值或阈值条件,若只使用`threshold`则等价于`keep >= threshold`,`threshold`类型为`INT64`<br/> `ignoreNull`:可选,默认为`true`;为`true`表示忽略null值,即如果中间出现null值,直接忽略,不会打断连续性;为`false`表示不忽略null值,即如果中间出现null值,会打断连续性 | INT64 |
+| 函数名 | 功能描述 | 允许的输入类型 | 必要的属性参数 | 输出类型 |
+|---------------|-----------------------------------------------|--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
+| SUM | 求和。 | INT32 INT64 FLOAT DOUBLE | 无 | DOUBLE |
+| COUNT | 计算数据点数。 | 所有类型 | 无 | INT64 |
+| AVG | 求平均值。 | INT32 INT64 FLOAT DOUBLE | 无 | DOUBLE |
+| EXTREME | 求具有最大绝对值的值。如果正值和负值的最大绝对值相等,则返回正值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
+| MAX_VALUE | 求最大值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
+| MIN_VALUE | 求最小值。 | INT32 INT64 FLOAT DOUBLE | 无 | 与输入类型一致 |
+| FIRST_VALUE | 求时间戳最小的值。 | 所有类型 | 无 | 与输入类型一致 |
+| LAST_VALUE | 求时间戳最大的值。 | 所有类型 | 无 | 与输入类型一致 |
+| MAX_TIME | 求最大时间戳。 | 所有类型 | 无 | Timestamp |
+| MIN_TIME | 求最小时间戳。 | 所有类型 | 无 | Timestamp |
+| COUNT_IF | 求数据点连续满足某一给定条件,且满足条件的数据点个数(用keep表示)满足指定阈值的次数。 | BOOLEAN | `[keep >=/>/=/!=/</<=]threshold`:被指定的阈值或阈值条件,若只使用`threshold`则等价于`keep >= threshold`,`threshold`类型为`INT64`<br/> `ignoreNull`:可选,默认为`true`;为`true`表示忽略null值,即如果中间出现null值,直接忽略,不会打断连续性;为`false`表示不忽略null值,即如果中间出现null值,会打断连续性 | INT64 |
+| TIME_DURATION | 求某一列最大一个不为NULL的值所在时间戳与最小一个不为NULL的值所在时间戳的时间戳差 | 所有类型 | 无 | INT64 |
### COUNT_IF
@@ -104,4 +105,57 @@ select count_if(s1=0 & s2=0, 3, 'ignoreNull'='false'), count_if(s1=1 & s2=0, 3,
+------------------------------------------------------------------------+------------------------------------------------------------------------+
| 1| 1|
+------------------------------------------------------------------------+------------------------------------------------------------------------+
+```
+
+## TIME_DURATION
+### 语法
+```sql
+ time_duration(Path)
+```
+### 使用示例
+#### 准备数据
+```
++----------+-------------+
+| Time|root.db.d1.s1|
++----------+-------------+
+| 1| 70|
+| 3| 10|
+| 4| 303|
+| 6| 110|
+| 7| 302|
+| 8| 110|
+| 9| 60|
+| 10| 70|
+|1677570934| 30|
++----------+-------------+
+```
+#### 写入语句
+```sql
+"CREATE DATABASE root.db",
+"CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+"INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+```
+
+查询:
+```sql
+select time_duration(s1) from root.db.d1.s1
+```
+
+输出
+```
++----------------------------+
+|time_duration(root.db.d1.s1)|
++----------------------------+
+| 1677570933|
++----------------------------+
```
\ No newline at end of file
diff --git a/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java b/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
index 86fcefc4a0..1ae9ca784a 100644
--- a/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
+++ b/integration-test/src/main/java/org/apache/iotdb/itbase/constant/TestConstant.java
@@ -111,6 +111,10 @@ public class TestConstant {
return String.format("min_value(%s)", path);
}
+ public static String timeDuration(String path) {
+ return String.format("time_duration(%s)", path);
+ }
+
public static String recordToInsert(TSRecord record) {
StringBuilder measurements = new StringBuilder();
StringBuilder values = new StringBuilder();
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
index f7573a9163..9f01ab179f 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBAggregationOptimizeIT.java
@@ -43,6 +43,7 @@ import static org.apache.iotdb.itbase.constant.TestConstant.maxValue;
import static org.apache.iotdb.itbase.constant.TestConstant.minTime;
import static org.apache.iotdb.itbase.constant.TestConstant.minValue;
import static org.apache.iotdb.itbase.constant.TestConstant.sum;
+import static org.apache.iotdb.itbase.constant.TestConstant.timeDuration;
@RunWith(IoTDBTestRunner.class)
@Category({LocalStandaloneIT.class, ClusterIT.class})
@@ -167,14 +168,15 @@ public class IoTDBAggregationOptimizeIT {
lastValue("root.test.1region_d1.s1"),
maxTime("root.test.1region_d1.s1"),
minTime("root.test.1region_d1.s1"),
- count("root.test.2region_d1.s1")
+ count("root.test.2region_d1.s1"),
+ timeDuration("root.test.2region_d1.s1")
};
- String[] retArray = new String[] {"2,3.0,1.5,2,2,1,1,2,2,1,2,"};
+ String[] retArray = new String[] {"2,3.0,1.5,2,2,1,1,2,2,1,2,999999999999,"};
resultSetEqualWithDescOrderTest(
"select count(1region_d1.s1),sum(1region_d1.s1),avg(1region_d1.s1),"
+ "extreme(1region_d1.s1),max_value(1region_d1.s1),min_value(1region_d1.s1),"
+ "first_value(1region_d1.s1),last_value(1region_d1.s1),max_time(1region_d1.s1),"
- + "min_time(1region_d1.s1),count(2region_d1.s1) from root.test",
+ + "min_time(1region_d1.s1),count(2region_d1.s1),time_duration(2region_d1.s1) from root.test",
expectedHeader,
retArray);
}
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java
new file mode 100644
index 0000000000..0be9aa7410
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/aggregation/IoTDBTimeDurationIT.java
@@ -0,0 +1,368 @@
+/*
+ * 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.it.aggregation;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.assertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareData;
+import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBTimeDurationIT {
+ // 2 devices 4 regions
+ protected static final String[] SQLs =
+ new String[] {
+ "CREATE DATABASE root.db",
+ "CREATE DATABASE root.db1",
+ "CREATE TIMESERIES root.db.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Beijing)",
+ "CREATE TIMESERIES root.db.d1.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db.d1.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db.d2.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db.d2.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db.d2.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d1.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d1.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d1.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d2.s1 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d2.s2 WITH DATATYPE=INT32, ENCODING=PLAIN tags(city=Nanjing)",
+ "CREATE TIMESERIES root.db1.d2.s3 WITH DATATYPE=BOOLEAN, ENCODING=PLAIN tags(city=Nanjing)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 2, 10, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(2, null, 20, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(3, 10, 0, null)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(4, 303, 30, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(5, null, 20, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(6, 110, 20, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(7, 302, 20, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(8, 110, null, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(9, 60, 20, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(10,70, 20, null)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1677570934, 30, 0, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(1, 80, 30, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(2, null, 30, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(3, 60, 30, null)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(4, 40, 20, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(5, null, 40, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(6, 40, 50, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(7, 40, 60, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(8, 40, null, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(9, 50, 70, true)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(10, 60, 80, null)",
+ "INSERT INTO root.db.d2(timestamp,s1,s2,s3) values(1677570934, 90, 90, true)",
+ "INSERT INTO root.db.d1(timestamp,s1,s2,s3) values(1, 70, 90, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(2, null, 80, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(3, 80, 70, null)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(4, 90, 70, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(5, null, 60, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(6, 20, 100, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(7, 10, 20, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(8, 20, null, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(9, 30, 20, true)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(10, 10, 20, null)",
+ "INSERT INTO root.db1.d1(timestamp,s1,s2,s3) values(1677570934, 0, 20, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(1, 10, 20, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(2, null, null, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(3, null, 20, null)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(4, 0, 0, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(5, null, 0, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(6, 0, 0, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(7, 1, 0, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(8, 1, null, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(9, null, 20, true)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(10, 1, 0, null)",
+ "INSERT INTO root.db1.d2(timestamp,s1,s2,s3) values(1677570939, 1, 0, true)",
+ "flush"
+ };
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().getConfig().getCommonConfig().setPartitionInterval(1000);
+ EnvFactory.getEnv().initClusterEnvironment();
+ prepareData(SQLs);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void testTimeDurationSingleColumnOnOneDataRegion() {
+ // normal
+ String[] expectedHeader = new String[] {"time_duration(root.db.d1.s1)"};
+ String[] retArray = new String[] {"8,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10", expectedHeader, retArray);
+
+ // order by time desc
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10 order by time desc",
+ expectedHeader,
+ retArray);
+
+ // order by time asc
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10 order by time asc",
+ expectedHeader,
+ retArray);
+
+ // limit
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10 limit 3",
+ expectedHeader,
+ retArray);
+
+ // align by device
+ expectedHeader = new String[] {"Device", "time_duration(s1)"};
+ retArray = new String[] {"root.db.d1,8,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10 align by device",
+ expectedHeader,
+ retArray);
+
+ // align by time
+ expectedHeader = new String[] {"time_duration(root.db.d1.s1)"};
+ retArray = new String[] {"8,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db.d1 where time < 10 align by time",
+ expectedHeader,
+ retArray);
+
+ // group by level=1(database)
+ expectedHeader = new String[] {"time_duration(root.db1.*.s1),time_duration(root.db.*.s1)"};
+ retArray = new String[] {"8,8,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.** where time < 10 group by level=1",
+ expectedHeader,
+ retArray);
+
+ // group by level=2(device)
+ expectedHeader = new String[] {"time_duration(root.*.d1.s1)", "time_duration(root.*.d2.s1)"};
+ retArray = new String[] {"8,8,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.** where time < 10 group by level=2",
+ expectedHeader,
+ retArray);
+
+ // group by session
+ expectedHeader = new String[] {"Time", "time_duration(root.db1.d1.s1)"};
+ retArray = new String[] {"3,6,"};
+ // ignore = true
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db1.d1 where time<10 group by session(100ms)",
+ expectedHeader,
+ retArray);
+
+ // group by variation
+ expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+ retArray = new String[] {"1,0,", "3,null,", "4,0,", "6,0,", "7,0,", "8,0,", "9,null,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db1.d2 where time<10 group by variation(root.db.d1.s1)",
+ expectedHeader,
+ retArray);
+
+ // group by condition
+ expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+ retArray = new String[] {"1,9,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db1.d2 group by condition(root.db.d2.s1 > 10,KEEP>=1,ignoreNull=true)",
+ expectedHeader,
+ retArray);
+
+ // group by tags
+ expectedHeader = new String[] {"city", "time_duration(s1)"};
+ retArray = new String[] {"Nanjing,1677570938,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db1.d2 group by tags(city)", expectedHeader, retArray);
+
+ // group by time
+ expectedHeader = new String[] {"Time", "time_duration(root.db1.d2.s1)"};
+ retArray = new String[] {"1,3,", "6,2,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.db1.d2 group by time([1,10),5ms)",
+ expectedHeader,
+ retArray);
+ }
+
+ @Test
+ public void testTimeDurationManyColumnOnManyDataRegion() {
+ // normal
+ String[] expectedHeader =
+ new String[] {
+ "time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ String[] retArray =
+ new String[] {
+ "1677570931,1677570938,1677570933,1677570933,1677570932,1677570938,1677570933,1677570933,"
+ };
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.**", expectedHeader, retArray);
+
+ // order by time desc
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** order by time desc",
+ expectedHeader,
+ retArray);
+
+ // order by time asc
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** order by time asc",
+ expectedHeader,
+ retArray);
+
+ // limit
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** limit 3",
+ expectedHeader,
+ retArray);
+
+ // align by device
+ expectedHeader = new String[] {"Device", "time_duration(s1)", "time_duration(s2)"};
+ retArray =
+ new String[] {
+ "root.db.d1,1677570933,1677570933,",
+ "root.db.d2,1677570933,1677570933,",
+ "root.db1.d1,1677570931,1677570932,",
+ "root.db1.d2,1677570938,1677570938,"
+ };
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** align by device",
+ expectedHeader,
+ retArray);
+
+ // align by time
+ expectedHeader =
+ new String[] {
+ "time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ retArray =
+ new String[] {
+ "1677570931,1677570938,1677570933,1677570933,1677570932,1677570938,1677570933,1677570933,"
+ };
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** align by time",
+ expectedHeader,
+ retArray);
+
+ // group by level=1(database)
+ expectedHeader =
+ new String[] {
+ "time_duration(root.db1.*.s1),time_duration(root.db.*.s1),time_duration(root.db1.*.s2),time_duration(root.db.*.s2)"
+ };
+ retArray = new String[] {"1677570938,1677570933,1677570938,1677570933,"};
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by level=1",
+ expectedHeader,
+ retArray);
+
+ // group by level=2(device)
+ expectedHeader =
+ new String[] {
+ "time_duration(root.*.d1.s1),time_duration(root.*.d2.s1),time_duration(root.*.d1.s2),time_duration(root.*.d2.s2)"
+ };
+ retArray = new String[] {"1677570933,1677570938,1677570933,1677570938,"};
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by level=2",
+ expectedHeader,
+ retArray);
+
+ // group by session
+ expectedHeader =
+ new String[] {
+ "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ retArray = new String[] {"1,7,9,9,9,8,9,9,9,", "1677570934,0,0,0,0,0,0,0,0,"};
+ // ignore = true
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by session(100ms)",
+ expectedHeader,
+ retArray);
+
+ // group by variation
+ expectedHeader =
+ new String[] {
+ "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ retArray =
+ new String[] {
+ "1,null,0,0,0,null,0,0,0,",
+ "3,0,null,0,0,0,0,0,0,",
+ "4,0,0,0,0,0,0,0,0,",
+ "6,0,0,0,0,0,0,0,0,",
+ "7,0,0,0,0,0,0,0,0,",
+ "8,0,0,0,0,null,null,null,null,",
+ "9,0,null,0,0,0,0,0,0,",
+ "10,0,0,0,0,0,0,0,0,",
+ "1677570934,0,null,0,0,0,null,0,0,"
+ };
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by variation(root.db.d1.s1)",
+ expectedHeader,
+ retArray);
+
+ // group by condition
+ expectedHeader =
+ new String[] {
+ "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ retArray =
+ new String[] {"1,1677570931,9,1677570933,1677570933,1677570931,9,1677570933,1677570933,"};
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by condition(root.db.d2.s1 > 10,KEEP>=1,ignoreNull=true)",
+ expectedHeader,
+ retArray);
+
+ // group by tags
+ expectedHeader = new String[] {"city", "time_duration(s1)"};
+ retArray = new String[] {"Beijing,1677570933,", "Nanjing,1677570938,"};
+ resultSetEqualTest(
+ "select time_duration(s1) from root.** group by tags(city)", expectedHeader, retArray);
+
+ // group by time
+ expectedHeader =
+ new String[] {
+ "Time,time_duration(root.db1.d1.s1),time_duration(root.db1.d2.s1),time_duration(root.db.d1.s1),time_duration(root.db.d2.s1),time_duration(root.db1.d1.s2),time_duration(root.db1.d2.s2),time_duration(root.db.d1.s2),time_duration(root.db.d2.s2)"
+ };
+ retArray = new String[] {"1,1,3,3,3,3,4,4,4,", "6,3,2,3,3,3,3,3,3,"};
+ resultSetEqualTest(
+ "select time_duration(s1),time_duration(s2) from root.** group by time([1,10),5ms)",
+ expectedHeader,
+ retArray);
+ }
+
+ @Test
+ public void testTimeDurationWithSlidingWindow() {
+ assertTestFail(
+ "select time_duration(s2) from root.db1.d2 group by ([1,10),5ms,2ms)",
+ TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode()
+ + ": TIME_DURATION with slidingWindow is not supported now");
+ }
+}
diff --git a/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
index e0977a7eb9..302750fe8f 100644
--- a/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
+++ b/node-commons/src/main/java/org/apache/iotdb/commons/udf/builtin/BuiltinAggregationFunction.java
@@ -36,7 +36,7 @@ public enum BuiltinAggregationFunction {
AVG("avg"),
SUM("sum"),
COUNT_IF("count_if"),
- ;
+ TIME_DURATION("time_duration");
private final String functionName;
@@ -72,6 +72,7 @@ public enum BuiltinAggregationFunction {
case "count":
case "avg":
case "sum":
+ case "time_duration":
return true;
case "count_if":
return false;
@@ -96,6 +97,7 @@ public enum BuiltinAggregationFunction {
case "count":
case "avg":
case "sum":
+ case "time_duration":
return true;
case "count_if":
return false;
diff --git a/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java b/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
index 42404c86c7..d721244f50 100644
--- a/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
+++ b/server/src/main/java/org/apache/iotdb/db/constant/SqlConstant.java
@@ -59,6 +59,7 @@ public class SqlConstant {
public static final String DIFF = "diff";
public static final String LAST = "last";
+ public static final String TIME_DURATION = "time_duration";
public static String[] getSingleRootArray() {
return SINGLE_ROOT_ARRAY;
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
index b806962f2d..18e18a47b8 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/AccumulatorFactory.java
@@ -67,6 +67,8 @@ public class AccumulatorFactory {
return new CountIfAccumulator(
initKeepEvaluator(inputExpressions.get(1)),
Boolean.parseBoolean(inputAttributes.getOrDefault("ignoreNull", "true")));
+ case TIME_DURATION:
+ return new TimeDurationAccumulator();
default:
throw new IllegalArgumentException("Invalid Aggregation function: " + aggregationType);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java
new file mode 100644
index 0000000000..47633e0dbb
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/TimeDurationAccumulator.java
@@ -0,0 +1,124 @@
+/*
+ * 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.aggregation;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
+import org.apache.iotdb.tsfile.read.common.block.column.Column;
+import org.apache.iotdb.tsfile.read.common.block.column.ColumnBuilder;
+import org.apache.iotdb.tsfile.utils.BitMap;
+
+public class TimeDurationAccumulator implements Accumulator {
+ protected long minTime = Long.MAX_VALUE;
+ protected long maxTime = Long.MIN_VALUE;
+ protected boolean initResult = false;
+
+ @Override
+ public void addInput(Column[] column, BitMap bitMap, int lastIndex) {
+ for (int i = 0; i <= lastIndex; i++) {
+ if (bitMap != null && !bitMap.isMarked(i)) {
+ continue;
+ }
+ if (!column[1].isNull(i)) {
+ initResult = true;
+ updateMaxTime(column[0].getLong(i));
+ updateMinTime(column[0].getLong(i));
+ }
+ }
+ }
+
+ @Override
+ public void addIntermediate(Column[] partialResult) {
+ if (partialResult[0].isNull(0)) {
+ return;
+ }
+ initResult = true;
+ updateMaxTime(partialResult[0].getLong(0));
+ updateMinTime(partialResult[1].getLong(0));
+ }
+
+ @Override
+ public void addStatistics(Statistics statistics) {
+ updateMaxTime(statistics.getEndTime());
+ updateMinTime(statistics.getStartTime());
+ }
+
+ @Override
+ public void setFinal(Column finalResult) {
+ if (finalResult.isNull(0)) {
+ return;
+ }
+ initResult = true;
+ maxTime = finalResult.getLong(0);
+ minTime = 0L;
+ }
+
+ @Override
+ public void outputIntermediate(ColumnBuilder[] tsBlockBuilder) {
+ if (!initResult) {
+ tsBlockBuilder[0].appendNull();
+ tsBlockBuilder[1].appendNull();
+ } else {
+ tsBlockBuilder[0].writeLong(maxTime);
+ tsBlockBuilder[1].writeLong(minTime);
+ }
+ }
+
+ @Override
+ public void outputFinal(ColumnBuilder tsBlockBuilder) {
+ if (!initResult) {
+ tsBlockBuilder.appendNull();
+ } else {
+ tsBlockBuilder.writeLong(maxTime - minTime);
+ }
+ }
+
+ @Override
+ public void reset() {
+ initResult = false;
+ this.maxTime = 0L;
+ this.minTime = Long.MAX_VALUE;
+ }
+
+ @Override
+ public boolean hasFinalResult() {
+ return false;
+ }
+
+ @Override
+ public TSDataType[] getIntermediateType() {
+ return new TSDataType[] {TSDataType.INT64, TSDataType.INT64};
+ }
+
+ @Override
+ public TSDataType getFinalType() {
+ return TSDataType.INT64;
+ }
+
+ protected void updateMaxTime(long curTime) {
+ initResult = true;
+ maxTime = Math.max(maxTime, curTime);
+ }
+
+ protected void updateMinTime(long curTime) {
+ initResult = true;
+ minTime = Math.min(minTime, curTime);
+ }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
index 63ea59167c..3ce57b1c1b 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/aggregation/slidingwindow/SlidingWindowAggregatorFactory.java
@@ -152,6 +152,8 @@ public class SlidingWindowAggregatorFactory {
: new EmptyQueueSlidingWindowAggregator(accumulator, inputLocationList, step);
case COUNT_IF:
throw new SemanticException("COUNT_IF with slidingWindow is not supported now");
+ case TIME_DURATION:
+ throw new SemanticException("TIME_DURATION with slidingWindow is not supported now");
default:
throw new IllegalArgumentException("Invalid Aggregation Type: " + aggregationType);
}
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 b54854825f..07aa25ef6e 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
@@ -2398,6 +2398,7 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
case SqlConstant.EXTREME:
case SqlConstant.AVG:
case SqlConstant.SUM:
+ case SqlConstant.TIME_DURATION:
checkFunctionExpressionInputSize(
functionExpression.getExpressionString(),
functionExpression.getExpressions().size(),
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
index 2a09ecc0ae..06a53e16c2 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/planner/plan/parameter/AggregationDescriptor.java
@@ -141,6 +141,10 @@ public class AggregationDescriptor {
outputAggregationNames.add(SqlConstant.LAST_VALUE);
outputAggregationNames.add(SqlConstant.MAX_TIME);
break;
+ case TIME_DURATION:
+ outputAggregationNames.add(SqlConstant.MAX_TIME);
+ outputAggregationNames.add(SqlConstant.MIN_TIME);
+ break;
default:
outputAggregationNames.add(aggregationFuncName);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
index fc8c29ffa0..ce3c341a4f 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/SchemaUtils.java
@@ -124,6 +124,7 @@ public class SchemaUtils {
case SqlConstant.MIN_TIME:
case SqlConstant.MAX_TIME:
case SqlConstant.COUNT:
+ case SqlConstant.TIME_DURATION:
return TSDataType.INT64;
case SqlConstant.AVG:
case SqlConstant.SUM:
@@ -157,6 +158,7 @@ public class SchemaUtils {
case EXTREME:
case COUNT:
case AVG:
+ case TIME_DURATION:
return true;
default:
throw new IllegalArgumentException(
@@ -180,6 +182,8 @@ public class SchemaUtils {
return Collections.singletonList(TAggregationType.MAX_TIME);
case AVG:
return Arrays.asList(TAggregationType.COUNT, TAggregationType.SUM);
+ case TIME_DURATION:
+ return Arrays.asList(TAggregationType.MAX_TIME, TAggregationType.MIN_TIME);
case SUM:
case MIN_VALUE:
case MAX_VALUE:
diff --git a/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java b/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
index c295d519cc..e655f9b158 100644
--- a/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/utils/TypeInferenceUtils.java
@@ -130,6 +130,7 @@ public class TypeInferenceUtils {
case SqlConstant.MAX_TIME:
case SqlConstant.COUNT:
case SqlConstant.COUNT_IF:
+ case SqlConstant.TIME_DURATION:
return TSDataType.INT64;
case SqlConstant.MIN_VALUE:
case SqlConstant.LAST_VALUE:
@@ -166,6 +167,7 @@ public class TypeInferenceUtils {
case SqlConstant.MAX_TIME:
case SqlConstant.FIRST_VALUE:
case SqlConstant.LAST_VALUE:
+ case SqlConstant.TIME_DURATION:
return;
case SqlConstant.COUNT_IF:
if (dataType != TSDataType.BOOLEAN) {
@@ -201,6 +203,7 @@ public class TypeInferenceUtils {
case SqlConstant.MAX_TIME:
case SqlConstant.FIRST_VALUE:
case SqlConstant.LAST_VALUE:
+ case SqlConstant.TIME_DURATION:
return;
case SqlConstant.COUNT_IF:
Expression keepExpression = inputExpressions.get(1);
diff --git a/thrift-commons/src/main/thrift/common.thrift b/thrift-commons/src/main/thrift/common.thrift
index 994316271f..1f3e335d31 100644
--- a/thrift-commons/src/main/thrift/common.thrift
+++ b/thrift-commons/src/main/thrift/common.thrift
@@ -139,7 +139,8 @@ enum TAggregationType {
MAX_VALUE,
MIN_VALUE,
EXTREME,
- COUNT_IF
+ COUNT_IF,
+ TIME_DURATION
}
// for MLNode