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 2022/07/01 10:54:57 UTC
[iotdb] branch master updated: [IOTDB-3173] Support Between expression (#6483)
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 6924bef386 [IOTDB-3173] Support Between expression (#6483)
6924bef386 is described below
commit 6924bef386c87fe9d2854099df0728f8067e80f5
Author: Weihao Li <60...@users.noreply.github.com>
AuthorDate: Fri Jul 1 18:54:52 2022 +0800
[IOTDB-3173] Support Between expression (#6483)
---
.../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 1 +
.../antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 | 2 +
docs/UserGuide/Query-Data/Query-Filter.md | 7 +-
docs/zh/UserGuide/Query-Data/Query-Filter.md | 13 +-
.../apache/iotdb/db/it/IoTDBFilterBetweenIT.java | 179 +++++++++++
.../org/apache/iotdb/db/it/IoTDBFilterNullIT.java | 2 +-
.../org/apache/iotdb/db/it/IoTDBNestedQueryIT.java | 72 +++++
.../db/it/IoTDBSyntaxConventionIdentifierIT.java | 4 -
.../iotdb/db/integration/IoTDBNestedQueryIT.java | 38 +++
.../IoTDBSyntaxConventionIdentifierIT.java | 4 -
.../db/mpp/plan/analyze/ExpressionAnalyzer.java | 257 ++++++++++++++--
.../iotdb/db/mpp/plan/analyze/ExpressionUtils.java | 73 +++++
.../iotdb/db/mpp/plan/expression/Expression.java | 5 +-
.../db/mpp/plan/expression/ExpressionType.java | 2 +-
.../plan/expression/ternary/BetweenExpression.java | 110 +++++++
.../plan/expression/ternary/TernaryExpression.java | 336 +++++++++++++++++++++
.../iotdb/db/mpp/plan/parser/ASTVisitor.java | 17 +-
.../dag/transformer/Transformer.java | 21 ++
.../transformer/ternary/BetweenTransformer.java | 67 ++++
.../ternary/CompareTernaryTransformer.java | 86 ++++++
.../transformer/ternary/TernaryTransformer.java | 265 ++++++++++++++++
.../apache/iotdb/db/qp/sql/IoTDBSqlVisitor.java | 37 ++-
.../iotdb/tsfile/read/filter/TimeFilter.java | 12 +
.../tsfile/read/filter/factory/FilterFactory.java | 4 +
.../read/filter/factory/FilterSerializeId.java | 3 +-
.../iotdb/tsfile/read/filter/operator/Between.java | 141 +++++++++
26 files changed, 1717 insertions(+), 41 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 1dbb03f7ce..dd01a8e693 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
@@ -861,6 +861,7 @@ expression
| leftExpression=expression (PLUS | MINUS) rightExpression=expression
| leftExpression=expression (OPERATOR_GT | OPERATOR_GTE | OPERATOR_LT | OPERATOR_LTE | OPERATOR_SEQ | OPERATOR_DEQ | OPERATOR_NEQ) rightExpression=expression
| unaryBeforeRegularOrLikeExpression=expression (REGEXP | LIKE) STRING_LITERAL
+ | firstExpression=expression OPERATOR_NOT? OPERATOR_BETWEEN secondExpression=expression OPERATOR_AND thirdExpression=expression
| unaryBeforeIsNullExpression=expression OPERATOR_IS OPERATOR_NOT? NULL_LITERAL
| unaryBeforeInExpression=expression OPERATOR_NOT? (OPERATOR_IN | OPERATOR_CONTAINS) LR_BRACKET constant (COMMA constant)* RR_BRACKET
| leftExpression=expression OPERATOR_AND rightExpression=expression
diff --git a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
index 7e9b769e88..307622686e 100644
--- a/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
+++ b/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
@@ -773,6 +773,8 @@ OPERATOR_LT : '<';
OPERATOR_LTE : '<=';
OPERATOR_NEQ : '!=' | '<>';
+OPERATOR_BETWEEN : B E T W E E N;
+
OPERATOR_IS : I S;
OPERATOR_IN : I N;
diff --git a/docs/UserGuide/Query-Data/Query-Filter.md b/docs/UserGuide/Query-Data/Query-Filter.md
index d0d90ebbf3..91ef5722bf 100644
--- a/docs/UserGuide/Query-Data/Query-Filter.md
+++ b/docs/UserGuide/Query-Data/Query-Filter.md
@@ -74,10 +74,13 @@ An example is as follows:
select status from root.sg1.d1 where status = true;
````
-3. Select data for the interval (36.5,40]:
+3. Select data for the interval [36.5,40] or not:
```sql
- select temperature from root.sg1.d1 where temperature > 36.5 and temperature < 40;
+ select temperature from root.sg1.d1 where temperature between 36.5 and 40;
+ ````
+ ```sql
+ select temperature from root.sg1.d1 where temperature not between 36.5 and 40;
````
4. Select data with values within a specific range:
diff --git a/docs/zh/UserGuide/Query-Data/Query-Filter.md b/docs/zh/UserGuide/Query-Data/Query-Filter.md
index 569bc04b02..a53f1c7994 100644
--- a/docs/zh/UserGuide/Query-Data/Query-Filter.md
+++ b/docs/zh/UserGuide/Query-Data/Query-Filter.md
@@ -73,12 +73,15 @@
```sql
select status from root.sg1.d1 where status = true;
-3. 选择区间 (36.5,40] 的数据:
-
- ```sql
- select temperature from root.sg1.d1 where temperature > 36.5 and temperature < 40;
- ```
+3. 选择区间 [36.5,40] 内或之外的数据:
+ ```sql
+ select temperature from root.sg1.d1 where temperature between 36.5 and 40;
+ ````
+ ```sql
+ select temperature from root.sg1.d1 where temperature not between 36.5 and 40;
+ ````
+
4. 选择值在特定范围内的数据:
```sql
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterBetweenIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterBetweenIT.java
new file mode 100644
index 0000000000..802205b7c0
--- /dev/null
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterBetweenIT.java
@@ -0,0 +1,179 @@
+/*
+ * 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;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.env.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({ClusterIT.class}) // TODO After old StandAlone remove
+public class IoTDBFilterBetweenIT {
+ protected static final int ITERATION_TIMES = 10;
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().initBeforeTest();
+ createTimeSeries();
+ generateData();
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanAfterTest();
+ }
+
+ private static void createTimeSeries() {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("SET STORAGE GROUP TO root.vehicle");
+ statement.execute("CREATE TIMESERIES root.vehicle.d1.s1 with datatype=INT32,encoding=PLAIN");
+ statement.execute("CREATE TIMESERIES root.vehicle.d1.s2 with datatype=INT32,encoding=PLAIN");
+ statement.execute("CREATE TIMESERIES root.vehicle.d1.s3 with datatype=TEXT,encoding=PLAIN");
+ } catch (SQLException throwable) {
+ fail(throwable.getMessage());
+ }
+ }
+
+ private static void generateData() {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ for (int i = 1; i <= ITERATION_TIMES; ++i) {
+ statement.execute(
+ String.format(
+ "insert into root.vehicle.d1(timestamp,s1,s2,s3) values(%d,%d,%d,%s)", i, i, i, i));
+ }
+ } catch (SQLException throwable) {
+ fail(throwable.getMessage());
+ }
+ }
+
+ @Test
+ public void testBetweenExpression() {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ int start = 1, end = 5;
+ String query = "SELECT * FROM root.vehicle.d1 WHERE s1 BETWEEN " + start + " AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query =
+ "SELECT * FROM root.vehicle.d1 WHERE s1 NOT BETWEEN " // test not between
+ + (end + 1)
+ + " AND "
+ + ITERATION_TIMES;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE time BETWEEN " + start + " AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query =
+ "SELECT * FROM root.vehicle.d1 WHERE time NOT BETWEEN " // test not between
+ + (end + 1)
+ + " AND "
+ + ITERATION_TIMES;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE " + start + " BETWEEN time AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals("1", rs.getString("Time"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s3"));
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE " + start + " NOT BETWEEN time AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start + 1; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE " + start + " BETWEEN " + end + " AND time";
+ try (ResultSet rs = statement.executeQuery(query)) {
+ Assert.assertFalse(rs.next());
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE " + start + " NOT BETWEEN " + end + " AND time";
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ Assert.fail(e.getMessage());
+ }
+ }
+}
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterNullIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterNullIT.java
index 189ccc1596..173b181d47 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterNullIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBFilterNullIT.java
@@ -33,7 +33,7 @@ import java.sql.Statement;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
-@Category(ClusterIT.class)
+@Category(ClusterIT.class) // TODO After old StandAlone remove
public class IoTDBFilterNullIT {
private static final String[] createSqls =
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
index cf474e813d..4f1f57386c 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBNestedQueryIT.java
@@ -570,6 +570,78 @@ public class IoTDBNestedQueryIT {
}
}
+ @Test
+ public void testBetweenExpression() {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ int start = 2, end = 8;
+ String query = "SELECT * FROM root.vehicle.d1 WHERE s1 BETWEEN " + start + " AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query =
+ "SELECT * FROM root.vehicle.d1 WHERE s1 NOT BETWEEN " // test not between
+ + (end + 1)
+ + " AND "
+ + ITERATION_TIMES;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals("1", rs.getString("Time"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s3"));
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query = "SELECT * FROM root.vehicle.d1 WHERE time BETWEEN " + start + " AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query =
+ "SELECT * FROM root.vehicle.d1 WHERE time NOT BETWEEN " // test not between
+ + (end + 1)
+ + " AND "
+ + ITERATION_TIMES;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals("1", rs.getString("Time"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals("1", rs.getString("root.vehicle.d1.s3"));
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ Assert.fail(e.getMessage());
+ }
+ }
+
// todo: remove ignore after iotdb-3349 is merged
@Ignore
@Test
diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionIdentifierIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionIdentifierIT.java
index c57b77ef95..7084bff368 100644
--- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionIdentifierIT.java
+++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBSyntaxConventionIdentifierIT.java
@@ -58,7 +58,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] createNodeNames = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
@@ -71,7 +70,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] resultTimeseries = {
"root.sg1.d1.add",
"root.sg1.d1.as",
- "root.sg1.d1.between",
"root.sg1.d1.select",
"root.sg1.d1.drop_trigger",
"root.sg1.d1.REVOKE_USER_ROLE",
@@ -84,7 +82,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] selectNodeNames = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
@@ -97,7 +94,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] suffixInResultColumns = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBNestedQueryIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBNestedQueryIT.java
index d56ad09184..7f613da051 100644
--- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBNestedQueryIT.java
+++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBNestedQueryIT.java
@@ -592,6 +592,44 @@ public class IoTDBNestedQueryIT {
}
}
+ @Test
+ public void testBetweenExpression() {
+ try (Connection connection =
+ DriverManager.getConnection(
+ Config.IOTDB_URL_PREFIX + "127.0.0.1:6667/", "root", "root");
+ Statement statement = connection.createStatement()) {
+ int start = 1, end = 5;
+ String query = "SELECT * FROM root.vehicle.d1 WHERE s1 BETWEEN " + start + " AND " + end;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+
+ query =
+ "SELECT * FROM root.vehicle.d1 WHERE s1 NOT BETWEEN " // test not between
+ + (end + 1)
+ + " AND "
+ + ITERATION_TIMES;
+ try (ResultSet rs = statement.executeQuery(query)) {
+ for (int i = start; i <= end; i++) {
+ Assert.assertTrue(rs.next());
+ Assert.assertEquals(String.valueOf(i), rs.getString("Time"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s1"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s2"));
+ Assert.assertEquals(String.valueOf(i), rs.getString("root.vehicle.d1.s3"));
+ }
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ Assert.fail(e.getMessage());
+ }
+ }
+
@Test
public void testRegularLikeInExpressions() {
try (Connection connection =
diff --git a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java
index 2f2ec8923d..c962703832 100644
--- a/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java
+++ b/integration/src/test/java/org/apache/iotdb/db/integration/IoTDBSyntaxConventionIdentifierIT.java
@@ -57,7 +57,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] createNodeNames = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
@@ -70,7 +69,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] resultTimeseries = {
"root.sg1.d1.add",
"root.sg1.d1.as",
- "root.sg1.d1.between",
"root.sg1.d1.select",
"root.sg1.d1.drop_trigger",
"root.sg1.d1.REVOKE_USER_ROLE",
@@ -83,7 +81,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] selectNodeNames = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
@@ -96,7 +93,6 @@ public class IoTDBSyntaxConventionIdentifierIT {
String[] suffixInResultColumns = {
"add",
"as",
- "between",
"select",
"drop_trigger",
"REVOKE_USER_ROLE",
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java
index 229b6ebd3c..100e8f2390 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionAnalyzer.java
@@ -42,6 +42,8 @@ import org.apache.iotdb.db.mpp.plan.expression.leaf.LeafOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.TernaryExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
@@ -62,9 +64,14 @@ import java.util.List;
import java.util.stream.Collectors;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.cartesianProduct;
+import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.checkConstantSatisfy;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.constructTimeFilter;
+import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.getPairFromBetweenTimeFirst;
+import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.getPairFromBetweenTimeSecond;
+import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.getPairFromBetweenTimeThird;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.reconstructBinaryExpressions;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.reconstructFunctionExpressions;
+import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.reconstructTernaryExpressions;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.reconstructTimeSeriesOperands;
import static org.apache.iotdb.db.mpp.plan.analyze.ExpressionUtils.reconstructUnaryExpressions;
@@ -77,7 +84,11 @@ public class ExpressionAnalyzer {
* @param expression expression to be checked
*/
public static void checkIsAllMeasurement(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ checkIsAllMeasurement(((TernaryExpression) expression).getFirstExpression());
+ checkIsAllMeasurement(((TernaryExpression) expression).getSecondExpression());
+ checkIsAllMeasurement(((TernaryExpression) expression).getThirdExpression());
+ } else if (expression instanceof BinaryExpression) {
checkIsAllMeasurement(((BinaryExpression) expression).getLeftExpression());
checkIsAllMeasurement(((BinaryExpression) expression).getRightExpression());
} else if (expression instanceof UnaryExpression) {
@@ -108,7 +119,32 @@ public class ExpressionAnalyzer {
* @return true if this expression is valid
*/
public static ResultColumn.ColumnType identifyOutputColumnType(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ ResultColumn.ColumnType firstType =
+ identifyOutputColumnType(((TernaryExpression) expression).getFirstExpression());
+ ResultColumn.ColumnType secondType =
+ identifyOutputColumnType(((TernaryExpression) expression).getSecondExpression());
+ ResultColumn.ColumnType thirdType =
+ identifyOutputColumnType(((TernaryExpression) expression).getThirdExpression());
+ boolean rawFlag = false, aggregationFlag = false;
+ if (firstType == ResultColumn.ColumnType.RAW
+ || secondType == ResultColumn.ColumnType.RAW
+ || thirdType == ResultColumn.ColumnType.RAW) rawFlag = true;
+ if (firstType == ResultColumn.ColumnType.AGGREGATION
+ || secondType == ResultColumn.ColumnType.AGGREGATION
+ || thirdType == ResultColumn.ColumnType.AGGREGATION) aggregationFlag = true;
+ if (rawFlag && aggregationFlag)
+ throw new SemanticException(
+ "Raw data and aggregation result hybrid calculation is not supported.");
+ if (firstType == ResultColumn.ColumnType.CONSTANT
+ && secondType == ResultColumn.ColumnType.CONSTANT
+ && thirdType == ResultColumn.ColumnType.CONSTANT) {
+ throw new SemanticException("Constant column is not supported.");
+ }
+ if (firstType != ResultColumn.ColumnType.CONSTANT) return firstType;
+ if (secondType != ResultColumn.ColumnType.CONSTANT) return secondType;
+ return thirdType;
+ } else if (expression instanceof BinaryExpression) {
ResultColumn.ColumnType leftType =
identifyOutputColumnType(((BinaryExpression) expression).getLeftExpression());
ResultColumn.ColumnType rightType =
@@ -162,7 +198,19 @@ public class ExpressionAnalyzer {
*/
public static List<Expression> concatExpressionWithSuffixPaths(
Expression expression, List<PartialPath> prefixPaths, PathPatternTree patternTree) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ List<Expression> firstExpressions =
+ concatExpressionWithSuffixPaths(
+ ((TernaryExpression) expression).getFirstExpression(), prefixPaths, patternTree);
+ List<Expression> secondExpressions =
+ concatExpressionWithSuffixPaths(
+ ((TernaryExpression) expression).getSecondExpression(), prefixPaths, patternTree);
+ List<Expression> thirdExpressions =
+ concatExpressionWithSuffixPaths(
+ ((TernaryExpression) expression).getThirdExpression(), prefixPaths, patternTree);
+ return reconstructTernaryExpressions(
+ expression, firstExpressions, secondExpressions, thirdExpressions);
+ } else if (expression instanceof BinaryExpression) {
List<Expression> leftExpressions =
concatExpressionWithSuffixPaths(
((BinaryExpression) expression).getLeftExpression(), prefixPaths, patternTree);
@@ -218,11 +266,25 @@ public class ExpressionAnalyzer {
*/
public static void constructPatternTreeFromExpression(
Expression predicate, List<PartialPath> prefixPaths, PathPatternTree patternTree) {
- if (predicate instanceof BinaryExpression) {
+ if (predicate instanceof TernaryExpression) {
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getFirstExpression(), prefixPaths, patternTree);
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getSecondExpression(), prefixPaths, patternTree);
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getThirdExpression(), prefixPaths, patternTree);
+ } else if (predicate instanceof BinaryExpression) {
constructPatternTreeFromExpression(
((BinaryExpression) predicate).getLeftExpression(), prefixPaths, patternTree);
constructPatternTreeFromExpression(
((BinaryExpression) predicate).getRightExpression(), prefixPaths, patternTree);
+ } else if (predicate instanceof TernaryExpression) {
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getFirstExpression(), prefixPaths, patternTree);
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getSecondExpression(), prefixPaths, patternTree);
+ constructPatternTreeFromExpression(
+ ((TernaryExpression) predicate).getThirdExpression(), prefixPaths, patternTree);
} else if (predicate instanceof UnaryExpression) {
constructPatternTreeFromExpression(
((UnaryExpression) predicate).getExpression(), prefixPaths, patternTree);
@@ -257,7 +319,19 @@ public class ExpressionAnalyzer {
*/
public static List<Expression> removeWildcardInExpression(
Expression expression, SchemaTree schemaTree) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ List<Expression> firstExpressions =
+ removeWildcardInExpression(
+ ((TernaryExpression) expression).getFirstExpression(), schemaTree);
+ List<Expression> secondExpressions =
+ removeWildcardInExpression(
+ ((TernaryExpression) expression).getSecondExpression(), schemaTree);
+ List<Expression> thirdExpressions =
+ removeWildcardInExpression(
+ ((TernaryExpression) expression).getThirdExpression(), schemaTree);
+ return reconstructTernaryExpressions(
+ expression, firstExpressions, secondExpressions, thirdExpressions);
+ } else if (expression instanceof BinaryExpression) {
List<Expression> leftExpressions =
removeWildcardInExpression(
((BinaryExpression) expression).getLeftExpression(), schemaTree);
@@ -320,7 +394,28 @@ public class ExpressionAnalyzer {
List<PartialPath> prefixPaths,
SchemaTree schemaTree,
TypeProvider typeProvider) {
- if (predicate instanceof BinaryExpression) {
+ if (predicate instanceof TernaryExpression) {
+ List<Expression> firstExpressions =
+ removeWildcardInQueryFilter(
+ ((TernaryExpression) predicate).getFirstExpression(),
+ prefixPaths,
+ schemaTree,
+ typeProvider);
+ List<Expression> secondExpressions =
+ removeWildcardInQueryFilter(
+ ((TernaryExpression) predicate).getSecondExpression(),
+ prefixPaths,
+ schemaTree,
+ typeProvider);
+ List<Expression> thirdExpressions =
+ removeWildcardInQueryFilter(
+ ((TernaryExpression) predicate).getThirdExpression(),
+ prefixPaths,
+ schemaTree,
+ typeProvider);
+ return reconstructTernaryExpressions(
+ predicate, firstExpressions, secondExpressions, thirdExpressions);
+ } else if (predicate instanceof BinaryExpression) {
List<Expression> leftExpressions =
removeWildcardInQueryFilter(
((BinaryExpression) predicate).getLeftExpression(),
@@ -402,7 +497,28 @@ public class ExpressionAnalyzer {
PartialPath devicePath,
SchemaTree schemaTree,
TypeProvider typeProvider) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ List<Expression> firstExpressions =
+ concatDeviceAndRemoveWildcard(
+ ((TernaryExpression) expression).getFirstExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ List<Expression> secondExpressions =
+ concatDeviceAndRemoveWildcard(
+ ((TernaryExpression) expression).getSecondExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ List<Expression> thirdExpressions =
+ concatDeviceAndRemoveWildcard(
+ ((TernaryExpression) expression).getThirdExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ return reconstructTernaryExpressions(
+ expression, firstExpressions, secondExpressions, thirdExpressions);
+ } else if (expression instanceof BinaryExpression) {
List<Expression> leftExpressions =
concatDeviceAndRemoveWildcard(
((BinaryExpression) expression).getLeftExpression(),
@@ -467,7 +583,28 @@ public class ExpressionAnalyzer {
PartialPath devicePath,
SchemaTree schemaTree,
TypeProvider typeProvider) {
- if (predicate instanceof BinaryExpression) {
+ if (predicate instanceof TernaryExpression) {
+ List<Expression> firstExpressions =
+ removeWildcardInQueryFilterByDevice(
+ ((TernaryExpression) predicate).getFirstExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ List<Expression> secondExpressions =
+ removeWildcardInQueryFilterByDevice(
+ ((TernaryExpression) predicate).getSecondExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ List<Expression> thirdExpressions =
+ removeWildcardInQueryFilterByDevice(
+ ((TernaryExpression) predicate).getThirdExpression(),
+ devicePath,
+ schemaTree,
+ typeProvider);
+ return reconstructTernaryExpressions(
+ predicate, firstExpressions, secondExpressions, thirdExpressions);
+ } else if (predicate instanceof BinaryExpression) {
List<Expression> leftExpressions =
removeWildcardInQueryFilterByDevice(
((BinaryExpression) predicate).getLeftExpression(),
@@ -590,6 +727,29 @@ public class ExpressionAnalyzer {
return new Pair<>(timeInRightFilter, false);
}
return new Pair<>(null, true);
+ } else if (predicate instanceof LikeExpression || predicate instanceof RegularExpression) {
+ return new Pair<>(null, true);
+ } else if (predicate instanceof BetweenExpression) {
+ Expression firstExpression = ((TernaryExpression) predicate).getFirstExpression();
+ Expression secondExpression = ((TernaryExpression) predicate).getSecondExpression();
+ Expression thirdExpression = ((TernaryExpression) predicate).getThirdExpression();
+ if (firstExpression instanceof TimestampOperand) {
+ return getPairFromBetweenTimeFirst(
+ secondExpression, thirdExpression, ((BetweenExpression) predicate).isNotBetween());
+ } else if (secondExpression instanceof TimestampOperand) {
+ if (checkConstantSatisfy(firstExpression, thirdExpression)) {
+ return getPairFromBetweenTimeSecond((BetweenExpression) predicate, firstExpression);
+ } else {
+ return new Pair<>(null, true); // TODO return Filter.True/False
+ }
+ } else if (thirdExpression instanceof TimestampOperand) {
+ if (checkConstantSatisfy(secondExpression, firstExpression)) {
+ return getPairFromBetweenTimeThird((BetweenExpression) predicate, firstExpression);
+ } else {
+ return new Pair<>(null, true); // TODO return Filter.True/False
+ }
+ }
+ return new Pair<>(null, true);
} else if (predicate instanceof IsNullExpression) {
return new Pair<>(null, true);
} else if (predicate instanceof InExpression) {
@@ -603,8 +763,6 @@ public class ExpressionAnalyzer {
false);
}
return new Pair<>(null, true);
- } else if (predicate instanceof LikeExpression || predicate instanceof RegularExpression) {
- return new Pair<>(null, true);
} else {
throw new IllegalArgumentException(
"unsupported expression type: " + predicate.getExpressionType());
@@ -621,7 +779,19 @@ public class ExpressionAnalyzer {
*/
public static List<Expression> searchSourceExpressions(
Expression expression, boolean isRawDataSource) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ List<Expression> resultExpressions = new ArrayList<>();
+ resultExpressions.addAll(
+ searchSourceExpressions(
+ ((TernaryExpression) expression).getFirstExpression(), isRawDataSource));
+ resultExpressions.addAll(
+ searchSourceExpressions(
+ ((TernaryExpression) expression).getSecondExpression(), isRawDataSource));
+ resultExpressions.addAll(
+ searchSourceExpressions(
+ ((TernaryExpression) expression).getThirdExpression(), isRawDataSource));
+ return resultExpressions;
+ } else if (expression instanceof BinaryExpression) {
List<Expression> resultExpressions = new ArrayList<>();
resultExpressions.addAll(
searchSourceExpressions(
@@ -659,7 +829,16 @@ public class ExpressionAnalyzer {
* @return searched aggregate functions list
*/
public static List<Expression> searchAggregationExpressions(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ List<Expression> resultExpressions = new ArrayList<>();
+ resultExpressions.addAll(
+ searchAggregationExpressions(((TernaryExpression) expression).getFirstExpression()));
+ resultExpressions.addAll(
+ searchAggregationExpressions(((TernaryExpression) expression).getSecondExpression()));
+ resultExpressions.addAll(
+ searchAggregationExpressions(((TernaryExpression) expression).getThirdExpression()));
+ return resultExpressions;
+ } else if (expression instanceof BinaryExpression) {
List<Expression> resultExpressions = new ArrayList<>();
resultExpressions.addAll(
searchAggregationExpressions(((BinaryExpression) expression).getLeftExpression()));
@@ -688,7 +867,11 @@ public class ExpressionAnalyzer {
/** Update typeProvider by expression. */
public static void updateTypeProvider(Expression expression, TypeProvider typeProvider) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ updateTypeProvider(((TernaryExpression) expression).getFirstExpression(), typeProvider);
+ updateTypeProvider(((TernaryExpression) expression).getSecondExpression(), typeProvider);
+ updateTypeProvider(((TernaryExpression) expression).getThirdExpression(), typeProvider);
+ } else if (expression instanceof BinaryExpression) {
updateTypeProvider(((BinaryExpression) expression).getLeftExpression(), typeProvider);
updateTypeProvider(((BinaryExpression) expression).getRightExpression(), typeProvider);
} else if (expression instanceof UnaryExpression) {
@@ -718,7 +901,20 @@ public class ExpressionAnalyzer {
* @return expression after removing alias
*/
public static Expression removeAliasFromExpression(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ Expression firstExpression =
+ removeAliasFromExpression(((TernaryExpression) expression).getFirstExpression());
+ Expression secondExpression =
+ removeAliasFromExpression(((TernaryExpression) expression).getFirstExpression());
+ Expression thirdExpression =
+ removeAliasFromExpression(((TernaryExpression) expression).getFirstExpression());
+ return reconstructTernaryExpressions(
+ expression,
+ Collections.singletonList(firstExpression),
+ Collections.singletonList(secondExpression),
+ Collections.singletonList(thirdExpression))
+ .get(0);
+ } else if (expression instanceof BinaryExpression) {
Expression leftExpression =
removeAliasFromExpression(((BinaryExpression) expression).getLeftExpression());
Expression rightExpression =
@@ -765,7 +961,9 @@ public class ExpressionAnalyzer {
/** Check for arithmetic expression, logical expression, UDF. Returns true if it exists. */
public static boolean checkIsNeedTransform(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ return true;
+ } else if (expression instanceof BinaryExpression) {
return true;
} else if (expression instanceof UnaryExpression) {
return true;
@@ -784,7 +982,19 @@ public class ExpressionAnalyzer {
/////////////////////////////////////////////////////////////////////////////////////////////////
public static String getDeviceNameInSourceExpression(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ String DeviceName =
+ getDeviceNameInSourceExpression(((TernaryExpression) expression).getFirstExpression());
+ if (DeviceName == null) {
+ DeviceName =
+ getDeviceNameInSourceExpression(((TernaryExpression) expression).getFirstExpression());
+ }
+ if (DeviceName == null) {
+ DeviceName =
+ getDeviceNameInSourceExpression(((TernaryExpression) expression).getThirdExpression());
+ }
+ return DeviceName;
+ } else if (expression instanceof BinaryExpression) {
String leftDeviceName =
getDeviceNameInSourceExpression(((BinaryExpression) expression).getLeftExpression());
if (leftDeviceName != null) {
@@ -806,7 +1016,20 @@ public class ExpressionAnalyzer {
}
public static Expression getMeasurementExpression(Expression expression) {
- if (expression instanceof BinaryExpression) {
+ if (expression instanceof TernaryExpression) {
+ Expression firstExpression =
+ getMeasurementExpression(((TernaryExpression) expression).getFirstExpression());
+ Expression secondExpression =
+ getMeasurementExpression(((TernaryExpression) expression).getFirstExpression());
+ Expression thirdExpression =
+ getMeasurementExpression(((TernaryExpression) expression).getFirstExpression());
+ return reconstructTernaryExpressions(
+ expression,
+ Collections.singletonList(firstExpression),
+ Collections.singletonList(secondExpression),
+ Collections.singletonList(thirdExpression))
+ .get(0);
+ } else if (expression instanceof BinaryExpression) {
Expression leftExpression =
getMeasurementExpression(((BinaryExpression) expression).getLeftExpression());
Expression rightExpression =
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java
index 2e6b3ea69b..f1b0df13ef 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/analyze/ExpressionUtils.java
@@ -39,6 +39,7 @@ import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
@@ -49,6 +50,7 @@ import org.apache.iotdb.db.mpp.plan.expression.unary.UnaryExpression;
import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
import org.apache.iotdb.tsfile.read.filter.TimeFilter;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
+import org.apache.iotdb.tsfile.utils.Pair;
import java.util.ArrayList;
import java.util.List;
@@ -176,6 +178,29 @@ public class ExpressionUtils {
return resultExpressions;
}
+ public static List<Expression> reconstructTernaryExpressions(
+ Expression expression,
+ List<Expression> firstExpressions,
+ List<Expression> secondExpressions,
+ List<Expression> thirdExpressions) {
+ List<Expression> resultExpressions = new ArrayList<>();
+ for (Expression fe : firstExpressions) {
+ for (Expression se : secondExpressions)
+ for (Expression te : thirdExpressions) {
+ switch (expression.getExpressionType()) {
+ case BETWEEN:
+ resultExpressions.add(
+ new BetweenExpression(
+ fe, se, te, ((BetweenExpression) expression).isNotBetween()));
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ return resultExpressions;
+ }
+
public static <T> void cartesianProduct(
List<List<T>> dimensionValue, List<List<T>> resultList, int layer, List<T> currentList) {
if (layer < dimensionValue.size() - 1) {
@@ -227,6 +252,54 @@ public class ExpressionUtils {
return null;
}
+ public static Pair<Filter, Boolean> getPairFromBetweenTimeFirst(
+ Expression firstExpression, Expression secondExpression, boolean not) {
+ if (firstExpression instanceof ConstantOperand
+ && secondExpression instanceof ConstantOperand
+ && ((ConstantOperand) firstExpression).getDataType() == TSDataType.INT64
+ && ((ConstantOperand) secondExpression).getDataType() == TSDataType.INT64) {
+ long value1 = Long.parseLong(((ConstantOperand) firstExpression).getValueString());
+ long value2 = Long.parseLong(((ConstantOperand) secondExpression).getValueString());
+ return new Pair<>(TimeFilter.between(value1, value2, not), false);
+ } else {
+ return new Pair<>(null, true);
+ }
+ }
+
+ public static Pair<Filter, Boolean> getPairFromBetweenTimeSecond(
+ BetweenExpression predicate, Expression expression) {
+ if (predicate.isNotBetween()) {
+ return new Pair<>(
+ TimeFilter.gt(Long.parseLong(((ConstantOperand) expression).getValueString())), false);
+
+ } else {
+ return new Pair<>(
+ TimeFilter.ltEq(Long.parseLong(((ConstantOperand) expression).getValueString())), false);
+ }
+ }
+
+ public static Pair<Filter, Boolean> getPairFromBetweenTimeThird(
+ BetweenExpression predicate, Expression expression) {
+ if (predicate.isNotBetween()) {
+ return new Pair<>(
+ TimeFilter.lt(Long.parseLong(((ConstantOperand) expression).getValueString())), false);
+
+ } else {
+ return new Pair<>(
+ TimeFilter.gtEq(Long.parseLong(((ConstantOperand) expression).getValueString())), false);
+ }
+ }
+
+ public static boolean checkConstantSatisfy(
+ Expression firstExpression, Expression secondExpression) {
+ return firstExpression.isConstantOperand()
+ && secondExpression.isConstantOperand()
+ && ((ConstantOperand) firstExpression).getDataType() == TSDataType.INT64
+ && ((ConstantOperand) secondExpression).getDataType() == TSDataType.INT64
+ && (Long.parseLong(((ConstantOperand) firstExpression).getValueString())
+ <= Long.parseLong(((ConstantOperand) secondExpression).getValueString()));
+ }
+
public static Expression constructQueryFilter(List<Expression> expressions) {
if (expressions.size() == 1) {
return expressions.get(0);
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java
index 5ce5a38dbe..3dc95b9485 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/Expression.java
@@ -41,6 +41,7 @@ import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
@@ -381,9 +382,9 @@ public abstract class Expression {
expression = new IsNullExpression(byteBuffer);
break;
- /*case 16:
+ case 16:
expression = new BetweenExpression(byteBuffer);
- break;*/
+ break;
case 17:
expression = new InExpression(byteBuffer);
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java
index b8118709e4..0bcec3a15c 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ExpressionType.java
@@ -47,7 +47,7 @@ public enum ExpressionType {
IS_NULL((short) 15, (short) 475),
- // BETWEEN((short) 16, (short) 450),
+ BETWEEN((short) 16, (short) 450),
IN((short) 17, (short) 400),
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/BetweenExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/BetweenExpression.java
new file mode 100644
index 0000000000..294bf2edf9
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/BetweenExpression.java
@@ -0,0 +1,110 @@
+/*
+ *
+ * * 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.expression.ternary;
+
+import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider;
+import org.apache.iotdb.db.mpp.plan.expression.Expression;
+import org.apache.iotdb.db.mpp.plan.expression.ExpressionType;
+import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader;
+import org.apache.iotdb.db.mpp.transformation.dag.transformer.ternary.BetweenTransformer;
+import org.apache.iotdb.db.mpp.transformation.dag.transformer.ternary.TernaryTransformer;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class BetweenExpression extends TernaryExpression {
+ private final boolean isNotBetween;
+
+ public boolean isNotBetween() {
+ return isNotBetween;
+ }
+
+ public BetweenExpression(
+ Expression firstExpression,
+ Expression secondExpression,
+ Expression thirdExpression,
+ boolean isNotBetween) {
+ super(firstExpression, secondExpression, thirdExpression);
+ this.isNotBetween = isNotBetween;
+ }
+
+ public BetweenExpression(
+ Expression firstExpression, Expression secondExpression, Expression thirdExpression) {
+ super(firstExpression, secondExpression, thirdExpression);
+ this.isNotBetween = false;
+ }
+
+ public BetweenExpression(ByteBuffer byteBuffer) {
+ super(byteBuffer);
+ this.isNotBetween = ReadWriteIOUtils.readBool(byteBuffer);
+ }
+
+ @Override
+ protected TernaryTransformer constructTransformer(
+ LayerPointReader firstParentLayerPointReader,
+ LayerPointReader secondParentLayerPointReader,
+ LayerPointReader thirdParentLayerPointReader) {
+ return new BetweenTransformer(
+ firstParentLayerPointReader,
+ secondParentLayerPointReader,
+ thirdParentLayerPointReader,
+ isNotBetween);
+ }
+
+ @Override
+ protected String operator() {
+ return "between";
+ }
+
+ @Override
+ public TSDataType inferTypes(TypeProvider typeProvider) {
+ return TSDataType.BOOLEAN;
+ }
+
+ @Override
+ protected String getExpressionStringInternal() {
+ return firstExpression + " BETWEEN " + secondExpression + " AND " + thirdExpression;
+ }
+
+ @Override
+ public ExpressionType getExpressionType() {
+ return ExpressionType.BETWEEN;
+ }
+
+ protected void serialize(ByteBuffer byteBuffer) {
+ super.serialize(byteBuffer);
+ ReadWriteIOUtils.write(isNotBetween, byteBuffer);
+ }
+
+ @Override
+ protected void serialize(DataOutputStream stream) throws IOException {
+ super.serialize(stream);
+ ReadWriteIOUtils.write(isNotBetween, stream);
+ }
+
+ public Expression getExpression() {
+ return this;
+ }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/TernaryExpression.java b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/TernaryExpression.java
new file mode 100644
index 0000000000..f1498ed804
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/plan/expression/ternary/TernaryExpression.java
@@ -0,0 +1,336 @@
+/*
+ *
+ * * 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.expression.ternary;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.db.exception.query.LogicalOptimizeException;
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.mpp.plan.analyze.TypeProvider;
+import org.apache.iotdb.db.mpp.plan.expression.Expression;
+import org.apache.iotdb.db.mpp.plan.planner.plan.parameter.InputLocation;
+import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader;
+import org.apache.iotdb.db.mpp.transformation.dag.input.QueryDataSetInputLayer;
+import org.apache.iotdb.db.mpp.transformation.dag.intermediate.IntermediateLayer;
+import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnMultiReferenceIntermediateLayer;
+import org.apache.iotdb.db.mpp.transformation.dag.intermediate.SingleInputColumnSingleReferenceIntermediateLayer;
+import org.apache.iotdb.db.mpp.transformation.dag.memory.LayerMemoryAssigner;
+import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer;
+import org.apache.iotdb.db.mpp.transformation.dag.transformer.ternary.TernaryTransformer;
+import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFContext;
+import org.apache.iotdb.db.mpp.transformation.dag.udf.UDTFExecutor;
+import org.apache.iotdb.db.qp.physical.crud.UDTFPlan;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.time.ZoneId;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public abstract class TernaryExpression extends Expression {
+ protected final Expression firstExpression;
+ protected final Expression secondExpression;
+ protected final Expression thirdExpression;
+ // protected final boolean isNot = false;
+
+ public Expression getFirstExpression() {
+ return firstExpression;
+ }
+
+ public Expression getSecondExpression() {
+ return secondExpression;
+ }
+
+ public Expression getThirdExpression() {
+ return thirdExpression;
+ }
+
+ protected TernaryExpression(
+ Expression firstExpression, Expression secondExpression, Expression thirdExpression) {
+ this.firstExpression = firstExpression;
+ this.secondExpression = secondExpression;
+ this.thirdExpression = thirdExpression;
+ }
+
+ protected TernaryExpression(ByteBuffer byteBuffer) {
+ this.firstExpression = Expression.deserialize(byteBuffer);
+ this.secondExpression = Expression.deserialize(byteBuffer);
+ this.thirdExpression = Expression.deserialize(byteBuffer);
+ }
+
+ @Override
+ public boolean isConstantOperandInternal() {
+ return firstExpression.isConstantOperand()
+ && secondExpression.isConstantOperand()
+ && thirdExpression.isConstantOperand();
+ }
+
+ @Override
+ public boolean isTimeSeriesGeneratingFunctionExpression() {
+ return !isUserDefinedAggregationFunctionExpression();
+ }
+
+ @Override
+ public boolean isUserDefinedAggregationFunctionExpression() {
+ return firstExpression.isBuiltInAggregationFunctionExpression()
+ || secondExpression.isBuiltInAggregationFunctionExpression()
+ || thirdExpression.isUserDefinedAggregationFunctionExpression();
+ }
+
+ @Override
+ public List<Expression> getExpressions() {
+ return Arrays.asList(firstExpression, secondExpression, thirdExpression);
+ }
+
+ @Override
+ public final void concat(List<PartialPath> prefixPaths, List<Expression> resultExpressions) {
+ List<Expression> firstExpressions = new ArrayList<>();
+ firstExpression.concat(prefixPaths, firstExpressions);
+
+ List<Expression> secondExpressions = new ArrayList<>();
+ secondExpression.concat(prefixPaths, secondExpressions);
+
+ List<Expression> thirdExpressions = new ArrayList<>();
+ secondExpression.concat(prefixPaths, thirdExpressions);
+
+ reconstruct(firstExpressions, secondExpressions, thirdExpressions, resultExpressions);
+ }
+
+ @Override
+ public final void removeWildcards(
+ org.apache.iotdb.db.qp.utils.WildcardsRemover wildcardsRemover,
+ List<Expression> resultExpressions)
+ throws LogicalOptimizeException {
+ List<Expression> firstExpressions = new ArrayList<>();
+ firstExpression.removeWildcards(wildcardsRemover, firstExpressions);
+
+ List<Expression> secondExpressions = new ArrayList<>();
+ secondExpression.removeWildcards(wildcardsRemover, secondExpressions);
+
+ List<Expression> thirdExpressions = new ArrayList<>();
+ thirdExpression.removeWildcards(wildcardsRemover, secondExpressions);
+ reconstruct(firstExpressions, secondExpressions, thirdExpressions, resultExpressions);
+ }
+
+ private void reconstruct(
+ List<Expression> firstExpressions,
+ List<Expression> secondExpressions,
+ List<Expression> thirdExpressions,
+ List<Expression> resultExpressions) {
+ for (Expression fe : firstExpressions) {
+ for (Expression se : secondExpressions)
+ for (Expression te : thirdExpressions) {
+ switch (operator()) {
+ case "between":
+ resultExpressions.add(new BetweenExpression(fe, se, te));
+ break;
+ default:
+ throw new UnsupportedOperationException();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void collectPaths(Set<PartialPath> pathSet) {
+ firstExpression.collectPaths(pathSet);
+ secondExpression.collectPaths(pathSet);
+ thirdExpression.collectPaths(pathSet);
+ }
+
+ @Override
+ public void constructUdfExecutors(
+ Map<String, UDTFExecutor> expressionName2Executor, ZoneId zoneId) {
+ firstExpression.constructUdfExecutors(expressionName2Executor, zoneId);
+ secondExpression.constructUdfExecutors(expressionName2Executor, zoneId);
+ thirdExpression.constructUdfExecutors(expressionName2Executor, zoneId);
+ }
+
+ @Override
+ public void bindInputLayerColumnIndexWithExpression(UDTFPlan udtfPlan) {
+ firstExpression.bindInputLayerColumnIndexWithExpression(udtfPlan);
+ secondExpression.bindInputLayerColumnIndexWithExpression(udtfPlan);
+ thirdExpression.bindInputLayerColumnIndexWithExpression(udtfPlan);
+ inputColumnIndex = udtfPlan.getReaderIndexByExpressionName(toString());
+ }
+
+ @Override
+ public final void bindInputLayerColumnIndexWithExpression(
+ Map<String, List<InputLocation>> inputLocations) {
+ firstExpression.bindInputLayerColumnIndexWithExpression(inputLocations);
+ secondExpression.bindInputLayerColumnIndexWithExpression(inputLocations);
+ thirdExpression.bindInputLayerColumnIndexWithExpression(inputLocations);
+
+ final String digest = toString();
+ if (inputLocations.containsKey(digest)) {
+ inputColumnIndex = inputLocations.get(digest).get(0).getValueColumnIndex();
+ }
+ }
+
+ @Override
+ public void updateStatisticsForMemoryAssigner(LayerMemoryAssigner memoryAssigner) {
+ firstExpression.updateStatisticsForMemoryAssigner(memoryAssigner);
+ secondExpression.updateStatisticsForMemoryAssigner(memoryAssigner);
+ thirdExpression.updateStatisticsForMemoryAssigner(memoryAssigner);
+ memoryAssigner.increaseExpressionReference(this);
+ }
+
+ @Override
+ public IntermediateLayer constructIntermediateLayer(
+ long queryId,
+ UDTFContext udtfContext,
+ QueryDataSetInputLayer rawTimeSeriesInputLayer,
+ Map<Expression, IntermediateLayer> expressionIntermediateLayerMap,
+ Map<Expression, TSDataType> expressionDataTypeMap,
+ LayerMemoryAssigner memoryAssigner)
+ throws QueryProcessException, IOException {
+ if (!expressionIntermediateLayerMap.containsKey(this)) {
+ float memoryBudgetInMB = memoryAssigner.assign();
+
+ IntermediateLayer firstParentIntermediateLayer =
+ firstExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ expressionDataTypeMap,
+ memoryAssigner);
+ IntermediateLayer secondParentIntermediateLayer =
+ secondExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ expressionDataTypeMap,
+ memoryAssigner);
+ IntermediateLayer thirdParentIntermediateLayer =
+ thirdExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ expressionDataTypeMap,
+ memoryAssigner);
+ Transformer transformer =
+ constructTransformer(
+ firstParentIntermediateLayer.constructPointReader(),
+ secondParentIntermediateLayer.constructPointReader(),
+ thirdParentIntermediateLayer.constructPointReader());
+ expressionDataTypeMap.put(this, transformer.getDataType());
+
+ // SingleInputColumnMultiReferenceIntermediateLayer doesn't support ConstantLayerPointReader
+ // yet. And since a ConstantLayerPointReader won't produce too much IO,
+ // SingleInputColumnSingleReferenceIntermediateLayer could be a better choice.
+ expressionIntermediateLayerMap.put(
+ this,
+ memoryAssigner.getReference(this) == 1 || isConstantOperand()
+ ? new SingleInputColumnSingleReferenceIntermediateLayer(
+ this, queryId, memoryBudgetInMB, transformer)
+ : new SingleInputColumnMultiReferenceIntermediateLayer(
+ this, queryId, memoryBudgetInMB, transformer));
+ }
+
+ return expressionIntermediateLayerMap.get(this);
+ }
+
+ @Override
+ public IntermediateLayer constructIntermediateLayer(
+ long queryId,
+ UDTFContext udtfContext,
+ QueryDataSetInputLayer rawTimeSeriesInputLayer,
+ Map<Expression, IntermediateLayer> expressionIntermediateLayerMap,
+ TypeProvider typeProvider,
+ LayerMemoryAssigner memoryAssigner)
+ throws QueryProcessException, IOException {
+ if (!expressionIntermediateLayerMap.containsKey(this)) {
+ float memoryBudgetInMB = memoryAssigner.assign();
+
+ IntermediateLayer firstParentIntermediateLayer =
+ firstExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ typeProvider,
+ memoryAssigner);
+ IntermediateLayer secondParentIntermediateLayer =
+ secondExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ typeProvider,
+ memoryAssigner);
+ IntermediateLayer thirdParentIntermediateLayer =
+ thirdExpression.constructIntermediateLayer(
+ queryId,
+ udtfContext,
+ rawTimeSeriesInputLayer,
+ expressionIntermediateLayerMap,
+ typeProvider,
+ memoryAssigner);
+ Transformer transformer =
+ constructTransformer(
+ firstParentIntermediateLayer.constructPointReader(),
+ secondParentIntermediateLayer.constructPointReader(),
+ thirdParentIntermediateLayer.constructPointReader());
+
+ // SingleInputColumnMultiReferenceIntermediateLayer doesn't support ConstantLayerPointReader
+ // yet. And since a ConstantLayerPointReader won't produce too much IO,
+ // SingleInputColumnSingleReferenceIntermediateLayer could be a better choice.
+ expressionIntermediateLayerMap.put(
+ this,
+ memoryAssigner.getReference(this) == 1 || isConstantOperand()
+ ? new SingleInputColumnSingleReferenceIntermediateLayer(
+ this, queryId, memoryBudgetInMB, transformer)
+ : new SingleInputColumnMultiReferenceIntermediateLayer(
+ this, queryId, memoryBudgetInMB, transformer));
+ }
+
+ return expressionIntermediateLayerMap.get(this);
+ }
+
+ protected abstract TernaryTransformer constructTransformer(
+ LayerPointReader firstParentLayerPointReader,
+ LayerPointReader secondParentLayerPointReader,
+ LayerPointReader thirdParentLayerPointReader);
+
+ protected abstract String operator();
+
+ @Override
+ protected void serialize(ByteBuffer byteBuffer) {
+ Expression.serialize(firstExpression, byteBuffer);
+ Expression.serialize(secondExpression, byteBuffer);
+ Expression.serialize(thirdExpression, byteBuffer);
+ }
+
+ @Override
+ protected void serialize(DataOutputStream stream) throws IOException {
+ Expression.serialize(firstExpression, stream);
+ Expression.serialize(secondExpression, stream);
+ Expression.serialize(thirdExpression, stream);
+ }
+}
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 4418d11349..4812d928fb 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
@@ -51,6 +51,7 @@ import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
@@ -1993,6 +1994,20 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
return parseIsNullExpression(context, inWithoutNull);
}
+ if (context.firstExpression != null
+ && context.secondExpression != null
+ && context.thirdExpression != null) {
+ Expression firstExpression = parseExpression(context.firstExpression, inWithoutNull);
+ Expression secondExpression = parseExpression(context.secondExpression, inWithoutNull);
+ Expression thirdExpression = parseExpression(context.thirdExpression, inWithoutNull);
+
+ if (context.OPERATOR_BETWEEN() != null) {
+ return new BetweenExpression(
+ firstExpression, secondExpression, thirdExpression, context.OPERATOR_NOT() != null);
+ }
+ throw new UnsupportedOperationException();
+ }
+
if (context.unaryBeforeInExpression != null) {
return parseInExpression(context, inWithoutNull);
}
@@ -2108,8 +2123,6 @@ public class ASTVisitor extends IoTDBSqlParserBaseVisitor<Statement> {
} else if (constantContext.dateExpression() != null) {
return new ConstantOperand(
TSDataType.INT64, String.valueOf(parseDateExpression(constantContext.dateExpression())));
- /*} else if (constantContext.NULL_LITERAL() != null) {
- return new ConstantOperand(TSDataType.TEXT, "");*/
} else {
throw new SQLParserException("Unsupported constant operand: " + text);
}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java
index 28fb5f9cae..3bd7f927dd 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/Transformer.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.db.mpp.transformation.api.YieldableState;
import org.apache.iotdb.tsfile.utils.Binary;
import java.io.IOException;
+import java.util.Objects;
public abstract class Transformer implements LayerPointReader {
@@ -118,4 +119,24 @@ public abstract class Transformer implements LayerPointReader {
public final boolean isCurrentNull() {
return currentNull;
}
+
+ protected static int compare(CharSequence cs1, CharSequence cs2) {
+ if (Objects.requireNonNull(cs1) == Objects.requireNonNull(cs2)) {
+ return 0;
+ }
+
+ if (cs1.getClass() == cs2.getClass() && cs1 instanceof Comparable) {
+ return ((Comparable<Object>) cs1).compareTo(cs2);
+ }
+
+ for (int i = 0, len = Math.min(cs1.length(), cs2.length()); i < len; i++) {
+ char a = cs1.charAt(i);
+ char b = cs2.charAt(i);
+ if (a != b) {
+ return a - b;
+ }
+ }
+
+ return cs1.length() - cs2.length();
+ }
}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/BetweenTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/BetweenTransformer.java
new file mode 100644
index 0000000000..70f8ef0bba
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/BetweenTransformer.java
@@ -0,0 +1,67 @@
+/*
+ *
+ * * 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.transformation.dag.transformer.ternary;
+
+import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader;
+
+public class BetweenTransformer extends CompareTernaryTransformer {
+
+ boolean isNotBetween;
+
+ public BetweenTransformer(
+ LayerPointReader firstPointReader,
+ LayerPointReader secondPointReader,
+ LayerPointReader thirdPointReader,
+ boolean isNotBetween) {
+ super(firstPointReader, secondPointReader, thirdPointReader);
+ this.isNotBetween = isNotBetween;
+ }
+
+ @Override
+ protected Evaluator constructNumberEvaluator() {
+ return () ->
+ ((Double.compare(
+ castCurrentValueToDoubleOperand(firstPointReader, firstPointReaderDataType),
+ castCurrentValueToDoubleOperand(
+ secondPointReader, secondPointReaderDataType))
+ >= 0)
+ && (Double.compare(
+ castCurrentValueToDoubleOperand(firstPointReader, firstPointReaderDataType),
+ castCurrentValueToDoubleOperand(thirdPointReader, thirdPointReaderDataType))
+ <= 0))
+ ^ isNotBetween;
+ }
+
+ @Override
+ protected Evaluator constructTextEvaluator() {
+ return () ->
+ ((compare(
+ firstPointReader.currentBinary().getStringValue(),
+ secondPointReader.currentBinary().getStringValue())
+ >= 0)
+ && (compare(
+ firstPointReader.currentBinary().getStringValue(),
+ thirdPointReader.currentBinary().getStringValue())
+ <= 0))
+ ^ isNotBetween;
+ }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/CompareTernaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/CompareTernaryTransformer.java
new file mode 100644
index 0000000000..e53c10f5c6
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/CompareTernaryTransformer.java
@@ -0,0 +1,86 @@
+/*
+ *
+ * * 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.transformation.dag.transformer.ternary;
+
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader;
+import org.apache.iotdb.tsfile.exception.write.UnSupportedDataTypeException;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+
+import java.io.IOException;
+
+public abstract class CompareTernaryTransformer extends TernaryTransformer {
+
+ @FunctionalInterface
+ public interface Evaluator {
+
+ boolean evaluate() throws QueryProcessException, IOException;
+ }
+
+ protected final Evaluator evaluator;
+
+ protected CompareTernaryTransformer(
+ LayerPointReader firstPointReader,
+ LayerPointReader secondPointReader,
+ LayerPointReader thirdPointReader)
+ throws UnSupportedDataTypeException {
+ super(firstPointReader, secondPointReader, thirdPointReader);
+ evaluator =
+ TSDataType.TEXT.equals(firstPointReaderDataType)
+ ? constructTextEvaluator()
+ : constructNumberEvaluator();
+ }
+
+ protected abstract Evaluator constructNumberEvaluator();
+
+ protected abstract Evaluator constructTextEvaluator();
+
+ @Override
+ protected final void checkType() {
+ if ((firstPointReaderDataType).equals(secondPointReaderDataType)
+ && (firstPointReaderDataType).equals(thirdPointReaderDataType)) {
+ return;
+ }
+
+ if (firstPointReaderDataType.equals(TSDataType.BOOLEAN)
+ || secondPointReaderDataType.equals(TSDataType.BOOLEAN)
+ || thirdPointReaderDataType.equals(TSDataType.BOOLEAN)) {
+ throw new UnSupportedDataTypeException(TSDataType.BOOLEAN.toString());
+ }
+
+ if (firstPointReaderDataType.equals(TSDataType.TEXT)
+ || secondPointReaderDataType.equals(TSDataType.TEXT)
+ || thirdPointReaderDataType.equals(TSDataType.TEXT)) {
+ throw new UnSupportedDataTypeException(TSDataType.TEXT.toString());
+ }
+ }
+
+ @Override
+ protected final void transformAndCache() throws QueryProcessException, IOException {
+ cachedBoolean = evaluator.evaluate();
+ }
+
+ @Override
+ public TSDataType getDataType() {
+ return TSDataType.BOOLEAN;
+ }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/TernaryTransformer.java b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/TernaryTransformer.java
new file mode 100644
index 0000000000..1e72272cfc
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/transformation/dag/transformer/ternary/TernaryTransformer.java
@@ -0,0 +1,265 @@
+/*
+ *
+ * * 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.transformation.dag.transformer.ternary;
+
+import org.apache.iotdb.db.exception.query.QueryProcessException;
+import org.apache.iotdb.db.mpp.transformation.api.LayerPointReader;
+import org.apache.iotdb.db.mpp.transformation.api.YieldableState;
+import org.apache.iotdb.db.mpp.transformation.dag.transformer.Transformer;
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+
+import java.io.IOException;
+
+public abstract class TernaryTransformer extends Transformer {
+ protected final LayerPointReader firstPointReader;
+ protected final LayerPointReader secondPointReader;
+ protected final LayerPointReader thirdPointReader;
+
+ protected final TSDataType firstPointReaderDataType;
+ protected final TSDataType secondPointReaderDataType;
+ protected final TSDataType thirdPointReaderDataType;
+
+ protected final boolean isFirstPointReaderConstant;
+ protected final boolean isSecondPointReaderConstant;
+ protected final boolean isThirdPointReaderConstant;
+
+ protected final boolean isCurrentConstant;
+
+ @Override
+ protected YieldableState yieldValue() throws QueryProcessException, IOException {
+ final YieldableState firstYieldableState = firstPointReader.yield();
+ final YieldableState secondYieldableState = secondPointReader.yield();
+ final YieldableState thirdYieldableState = thirdPointReader.yield();
+
+ if (YieldableState.NOT_YIELDABLE_NO_MORE_DATA.equals(firstYieldableState)
+ || YieldableState.NOT_YIELDABLE_NO_MORE_DATA.equals(secondYieldableState)
+ || YieldableState.NOT_YIELDABLE_NO_MORE_DATA.equals(thirdYieldableState)) {
+ return YieldableState.NOT_YIELDABLE_NO_MORE_DATA;
+ }
+
+ if (YieldableState.NOT_YIELDABLE_WAITING_FOR_DATA.equals(firstYieldableState)
+ || YieldableState.NOT_YIELDABLE_WAITING_FOR_DATA.equals(secondYieldableState)
+ || YieldableState.NOT_YIELDABLE_WAITING_FOR_DATA.equals(thirdYieldableState)) {
+ return YieldableState.NOT_YIELDABLE_WAITING_FOR_DATA;
+ }
+
+ final YieldableState timeYieldState = yieldTime();
+ if (!YieldableState.YIELDABLE.equals(timeYieldState)) {
+ return timeYieldState;
+ }
+
+ if (firstPointReader.isCurrentNull()
+ || secondPointReader.isCurrentNull()
+ || thirdPointReader.isCurrentNull()) {
+ currentNull = true;
+ } else {
+ transformAndCache();
+ }
+
+ firstPointReader.readyForNext();
+ secondPointReader.readyForNext();
+ thirdPointReader.readyForNext();
+ return YieldableState.YIELDABLE;
+ }
+
+ private YieldableState yieldTime() throws IOException, QueryProcessException {
+ if (isCurrentConstant) {
+ return YieldableState.YIELDABLE;
+ }
+
+ long firstTime = isFirstPointReaderConstant ? Long.MIN_VALUE : firstPointReader.currentTime();
+ long secondTime =
+ isSecondPointReaderConstant ? Long.MIN_VALUE : secondPointReader.currentTime();
+ long thirdTime = isThirdPointReaderConstant ? Long.MIN_VALUE : thirdPointReader.currentTime();
+
+ while (firstTime != secondTime || firstTime != thirdTime) { // the logic is similar to MergeSort
+ if (firstTime < secondTime) {
+ if (isFirstPointReaderConstant) {
+ firstTime = secondTime;
+ } else {
+ firstPointReader.readyForNext();
+ final YieldableState firstYieldableState = firstPointReader.yield();
+ if (!YieldableState.YIELDABLE.equals(firstYieldableState)) {
+ return firstYieldableState;
+ }
+ firstTime = firstPointReader.currentTime();
+ }
+ } else if (secondTime < thirdTime) {
+ if (isSecondPointReaderConstant) {
+ secondTime = thirdTime;
+ } else {
+ secondPointReader.readyForNext();
+ final YieldableState secondYieldableState = secondPointReader.yield();
+ if (!YieldableState.YIELDABLE.equals(secondYieldableState)) {
+ return secondYieldableState;
+ }
+ secondTime = secondPointReader.currentTime();
+ }
+ } else {
+ if (isThirdPointReaderConstant) {
+ thirdTime = firstTime;
+ } else {
+ thirdPointReader.readyForNext();
+ final YieldableState thirdYieldableState = thirdPointReader.yield();
+ if (!YieldableState.YIELDABLE.equals(thirdYieldableState)) {
+ return thirdYieldableState;
+ }
+ thirdTime = thirdPointReader.currentTime();
+ }
+ }
+ }
+
+ if (firstTime != Long.MIN_VALUE) {
+ cachedTime = firstTime;
+ }
+ return YieldableState.YIELDABLE;
+ }
+
+ protected TernaryTransformer(
+ LayerPointReader firstPointReader,
+ LayerPointReader secondPointReader,
+ LayerPointReader thirdPointReader) {
+ this.firstPointReader = firstPointReader;
+ this.secondPointReader = secondPointReader;
+ this.thirdPointReader = thirdPointReader;
+ this.firstPointReaderDataType = firstPointReader.getDataType();
+ this.secondPointReaderDataType = secondPointReader.getDataType();
+ this.thirdPointReaderDataType = thirdPointReader.getDataType();
+ this.isFirstPointReaderConstant = firstPointReader.isConstantPointReader();
+ this.isSecondPointReaderConstant = secondPointReader.isConstantPointReader();
+ this.isThirdPointReaderConstant = thirdPointReader.isConstantPointReader();
+ this.isCurrentConstant =
+ isFirstPointReaderConstant && isSecondPointReaderConstant && isThirdPointReaderConstant;
+ checkType();
+ }
+
+ @Override
+ public boolean isConstantPointReader() {
+ return firstPointReader.isConstantPointReader()
+ && secondPointReader.isConstantPointReader()
+ && thirdPointReader.isConstantPointReader();
+ }
+
+ @Override
+ protected boolean cacheValue() throws QueryProcessException, IOException {
+ if (!firstPointReader.next() || !secondPointReader.next() || !thirdPointReader.next()) {
+ return false;
+ }
+
+ if (!cacheTime()) {
+ return false;
+ }
+
+ if (firstPointReader.isCurrentNull()
+ || secondPointReader.isCurrentNull()
+ || thirdPointReader.isCurrentNull()) {
+ currentNull = true;
+ } else {
+ transformAndCache();
+ }
+
+ firstPointReader.readyForNext();
+ secondPointReader.readyForNext();
+ thirdPointReader.readyForNext();
+ return true;
+ }
+
+ protected abstract void transformAndCache() throws QueryProcessException, IOException;
+
+ protected abstract void checkType();
+
+ /**
+ * finds the smallest, unconsumed, same timestamp that exists in {@code firstPointReader}, {@code
+ * secondPointReader} and {@code thirdPointReader}and then caches the timestamp in {@code
+ * cachedTime}.
+ *
+ * @return true if there has a timestamp that meets the requirements
+ */
+ private boolean cacheTime() throws IOException, QueryProcessException {
+ boolean isFirstConstant = firstPointReader.isConstantPointReader();
+ boolean isSecondConstant = secondPointReader.isConstantPointReader();
+ boolean isThirdConstant = thirdPointReader.isConstantPointReader();
+ long firstTime = isFirstConstant ? Long.MIN_VALUE : firstPointReader.currentTime();
+ long secondTime = isSecondConstant ? Long.MIN_VALUE : secondPointReader.currentTime();
+ long thirdTime = isThirdConstant ? Long.MIN_VALUE : secondPointReader.currentTime();
+ // Long.MIN_VALUE is used to determine whether isFirstConstant && isSecondConstant &&
+ // isThirdConstant = true
+ while (firstTime != secondTime || firstTime != thirdTime) { // the logic is similar to MergeSort
+ if (firstTime < secondTime) {
+ if (isFirstConstant) {
+ firstTime = secondTime;
+ } else {
+ firstPointReader.readyForNext();
+ if (!firstPointReader.next()) {
+ return false;
+ }
+ firstTime = firstPointReader.currentTime();
+ }
+ } else if (secondTime < thirdTime) {
+ if (isSecondConstant) {
+ secondTime = thirdTime;
+ } else {
+ secondPointReader.readyForNext();
+ if (!secondPointReader.next()) {
+ return false;
+ }
+ secondTime = secondPointReader.currentTime();
+ }
+ } else {
+ if (isThirdConstant) {
+ thirdTime = firstTime;
+ } else {
+ thirdPointReader.readyForNext();
+ if (!thirdPointReader.next()) {
+ return false;
+ }
+ thirdTime = secondPointReader.currentTime();
+ }
+ }
+ }
+
+ if (firstTime != Long.MIN_VALUE) {
+ cachedTime = firstTime;
+ }
+ return true;
+ }
+
+ protected static double castCurrentValueToDoubleOperand(
+ LayerPointReader layerPointReader, TSDataType layerPointReaderDataType)
+ throws IOException, QueryProcessException {
+ switch (layerPointReaderDataType) {
+ case INT32:
+ return layerPointReader.currentInt();
+ case INT64:
+ return layerPointReader.currentLong();
+ case FLOAT:
+ return layerPointReader.currentFloat();
+ case DOUBLE:
+ return layerPointReader.currentDouble();
+ case BOOLEAN:
+ return layerPointReader.currentBoolean() ? 1.0d : 0.0d;
+ default:
+ throw new QueryProcessException(
+ "Unsupported data type: " + layerPointReader.getDataType().toString());
+ }
+ }
+}
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 06a0daef9e..378646ea45 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
@@ -46,6 +46,8 @@ import org.apache.iotdb.db.mpp.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.mpp.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.mpp.plan.expression.multi.FunctionExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.BetweenExpression;
+import org.apache.iotdb.db.mpp.plan.expression.ternary.TernaryExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.InExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LikeExpression;
import org.apache.iotdb.db.mpp.plan.expression.unary.LogicNotExpression;
@@ -2857,6 +2859,19 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
throw new UnsupportedOperationException();
}
+ if (context.firstExpression != null
+ && context.secondExpression != null
+ && context.thirdExpression != null) {
+ Expression firstExpression = parseExpression(context.firstExpression, inWithoutNull);
+ Expression secondExpression = parseExpression(context.secondExpression, inWithoutNull);
+ Expression thirdExpression = parseExpression(context.thirdExpression, inWithoutNull);
+
+ if (context.OPERATOR_BETWEEN() != null) {
+ return new BetweenExpression(
+ firstExpression, secondExpression, thirdExpression, context.OPERATOR_NOT() != null);
+ }
+ }
+
if (context.unaryBeforeInExpression != null) {
return parseInExpression(context, inWithoutNull);
}
@@ -2881,8 +2896,7 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
throw new UnsupportedOperationException();
}
- private Expression parseExpression(
- IoTDBSqlParser.ExpressionContext context, boolean inWithoutNull) {
+ private Expression parseExpression(ExpressionContext context, boolean inWithoutNull) {
return parseExpression(context, inWithoutNull, false);
}
@@ -3066,6 +3080,25 @@ public class IoTDBSqlVisitor extends IoTDBSqlParserBaseVisitor<Operator> {
FilterType.REGEXP,
parsePathFromExpression(((RegularExpression) predicate).getExpression()),
((RegularExpression) predicate).getPatternString());
+ } else if (predicate instanceof BetweenExpression) {
+ filter = new FilterOperator(FilterType.KW_AND);
+ PartialPath partialPath =
+ parsePathFromExpression(((TernaryExpression) predicate).getFirstExpression());
+ filter.addChildOperator(
+ new BasicFunctionOperator(
+ FilterType.GREATERTHANOREQUALTO,
+ partialPath,
+ parseValueFromExpression(((TernaryExpression) predicate).getSecondExpression())));
+ filter.addChildOperator(
+ new BasicFunctionOperator(
+ FilterType.LESSTHANOREQUALTO,
+ partialPath,
+ parseValueFromExpression(((TernaryExpression) predicate).getThirdExpression())));
+ if (((BetweenExpression) predicate).isNotBetween()) {
+ FilterOperator temp = new FilterOperator(FilterType.KW_NOT);
+ temp.addChildOperator(filter);
+ filter = temp;
+ }
} else if (predicate instanceof InExpression) {
filter =
new InOperator(
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
index 3e1b7c0629..e5109bf46b 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/TimeFilter.java
@@ -20,6 +20,7 @@ package org.apache.iotdb.tsfile.read.filter;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
import org.apache.iotdb.tsfile.read.filter.factory.FilterType;
+import org.apache.iotdb.tsfile.read.filter.operator.Between;
import org.apache.iotdb.tsfile.read.filter.operator.Eq;
import org.apache.iotdb.tsfile.read.filter.operator.Gt;
import org.apache.iotdb.tsfile.read.filter.operator.GtEq;
@@ -67,6 +68,17 @@ public class TimeFilter {
return new TimeIn(values, not);
}
+ public static TimeBetween between(long value1, long value2, boolean not) {
+ return new TimeBetween(value1, value2, not);
+ }
+
+ public static class TimeBetween extends Between {
+
+ private TimeBetween(long value1, long value2, boolean not) {
+ super(value1, value2, FilterType.TIME_FILTER, not);
+ }
+ }
+
public static class TimeIn extends In {
private TimeIn(Set<Long> values, boolean not) {
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
index 11d9349a25..4dff4e6337 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterFactory.java
@@ -22,6 +22,7 @@ import org.apache.iotdb.tsfile.read.filter.GroupByFilter;
import org.apache.iotdb.tsfile.read.filter.GroupByMonthFilter;
import org.apache.iotdb.tsfile.read.filter.basic.Filter;
import org.apache.iotdb.tsfile.read.filter.operator.AndFilter;
+import org.apache.iotdb.tsfile.read.filter.operator.Between;
import org.apache.iotdb.tsfile.read.filter.operator.Eq;
import org.apache.iotdb.tsfile.read.filter.operator.Gt;
import org.apache.iotdb.tsfile.read.filter.operator.GtEq;
@@ -81,6 +82,9 @@ public class FilterFactory {
case LTEQ:
filter = new LtEq<>();
break;
+ case BETWEEN:
+ filter = new Between<>();
+ break;
case IN:
filter = new In<>();
break;
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
index 85b0a4f5fe..bb3c7e1060 100644
--- a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/factory/FilterSerializeId.java
@@ -33,5 +33,6 @@ public enum FilterSerializeId {
OR,
IN,
REGEXP,
- LIKE
+ LIKE,
+ BETWEEN
}
diff --git a/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Between.java b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Between.java
new file mode 100644
index 0000000000..0ca994f315
--- /dev/null
+++ b/tsfile/src/main/java/org/apache/iotdb/tsfile/read/filter/operator/Between.java
@@ -0,0 +1,141 @@
+/*
+ * 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.tsfile.read.filter.operator;
+
+import org.apache.iotdb.tsfile.file.metadata.enums.TSDataType;
+import org.apache.iotdb.tsfile.file.metadata.statistics.Statistics;
+import org.apache.iotdb.tsfile.read.filter.basic.Filter;
+import org.apache.iotdb.tsfile.read.filter.factory.FilterSerializeId;
+import org.apache.iotdb.tsfile.read.filter.factory.FilterType;
+import org.apache.iotdb.tsfile.utils.ReadWriteIOUtils;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+public class Between<T extends Comparable<T>> implements Filter {
+
+ private static final long serialVersionUID = -537390606419370764L;
+
+ protected T value1;
+
+ protected T value2;
+
+ protected boolean not;
+
+ protected FilterType filterType;
+
+ public Between() {}
+
+ public Between(T value1, T value2, FilterType filterType, boolean not) {
+ this.value1 = value1;
+ this.value2 = value2;
+ this.filterType = filterType;
+ this.not = not;
+ }
+
+ @Override
+ public void serialize(DataOutputStream outputStream) {
+ try {
+ outputStream.write(getSerializeId().ordinal());
+ outputStream.write(filterType.ordinal());
+ ReadWriteIOUtils.write(not, outputStream);
+ ReadWriteIOUtils.writeObject(value1, outputStream);
+ ReadWriteIOUtils.writeObject(value2, outputStream);
+ } catch (IOException ignored) {
+ // ignored
+ }
+ }
+
+ @Override
+ public void deserialize(ByteBuffer buffer) {
+ filterType = FilterType.values()[buffer.get()];
+ not = ReadWriteIOUtils.readBool(buffer);
+ value1 = (T) ReadWriteIOUtils.readObject(buffer);
+ value2 = (T) ReadWriteIOUtils.readObject(buffer);
+ }
+
+ @Override
+ public FilterSerializeId getSerializeId() {
+ return FilterSerializeId.BETWEEN;
+ }
+
+ @Override
+ public boolean satisfy(Statistics statistics) {
+ if (filterType == FilterType.TIME_FILTER) {
+ long time1 = (Long) value1, time2 = (Long) value2;
+ if (not) {
+ return statistics.getStartTime() < time1 || statistics.getEndTime() > time2;
+ } else {
+ return statistics.getEndTime() >= time1 || statistics.getStartTime() <= time2;
+ }
+ } else {
+ if (statistics.getType() == TSDataType.TEXT || statistics.getType() == TSDataType.BOOLEAN) {
+ return true;
+ }
+ if (not) {
+ return ((T) statistics.getMinValue()).compareTo(value1) < 0
+ || ((T) statistics.getMaxValue()).compareTo(value2) > 0;
+ } else {
+ return ((T) statistics.getMaxValue()).compareTo(value1) >= 0
+ && ((T) statistics.getMinValue()).compareTo(value2) <= 0;
+ }
+ }
+ }
+
+ @Override
+ public boolean satisfy(long time, Object value) {
+ Object v = filterType == FilterType.TIME_FILTER ? time : value;
+ return (value1.compareTo((T) v) <= 0 && ((T) v).compareTo(value2) <= 0) ^ not;
+ }
+
+ @Override
+ public boolean satisfyStartEndTime(long startTime, long endTime) {
+ if (filterType == FilterType.TIME_FILTER) {
+ long time1 = (Long) value1, time2 = (Long) value2;
+ if (not) {
+ return startTime < time1 || endTime > time2;
+ } else {
+ return endTime >= time1 && startTime <= time2;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public boolean containStartEndTime(long startTime, long endTime) {
+ if (filterType == FilterType.TIME_FILTER) {
+ long time1 = (Long) value1, time2 = (Long) value2;
+ if (not) {
+ return endTime < time1 || startTime > time2;
+ } else {
+ return startTime >= time1 && endTime <= time2;
+ }
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public Filter copy() {
+ return new Between(value1, value2, filterType, not);
+ }
+}