This is an automated email from the ASF dual-hosted git repository.
lancelly pushed a commit to branch support_uncorrelated_subquery_in_where
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to
refs/heads/support_uncorrelated_subquery_in_where by this push:
new 94185259ead add some ITs
94185259ead is described below
commit 94185259ead7de91349306e5b8e32c9b90d41c41
Author: lancelly <[email protected]>
AuthorDate: Wed Nov 20 14:34:14 2024 +0800
add some ITs
---
.../IoTDBUncorrelatedSubqueryInWhereClauseIT.java | 269 +++++++++++++++++++++
.../recent/subquery/SubqueryDataSetUtils.java | 110 +++++++++
.../operator/process/EnforceSingleRowOperator.java | 115 +++++++++
.../plan/planner/TableOperatorGenerator.java | 19 ++
.../distribute/TableDistributedPlanGenerator.java | 13 +
.../optimizations/PushPredicateIntoTableScan.java | 24 +-
6 files changed, 546 insertions(+), 4 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/IoTDBUncorrelatedSubqueryInWhereClauseIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/IoTDBUncorrelatedSubqueryInWhereClauseIT.java
new file mode 100644
index 00000000000..a71b1964899
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/IoTDBUncorrelatedSubqueryInWhereClauseIT.java
@@ -0,0 +1,269 @@
+/*
+ * 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.relational.it.query.recent.subquery;
+
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import static org.apache.iotdb.db.it.utils.TestUtils.prepareTableData;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail;
+import static org.apache.iotdb.db.it.utils.TestUtils.tableResultSetEqualTest;
+import static
org.apache.iotdb.relational.it.query.recent.subquery.SubqueryDataSetUtils.CREATE_SQLS;
+import static
org.apache.iotdb.relational.it.query.recent.subquery.SubqueryDataSetUtils.DATABASE_NAME;
+import static
org.apache.iotdb.relational.it.query.recent.subquery.SubqueryDataSetUtils.NUMERIC_MEASUREMENTS;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBUncorrelatedSubqueryInWhereClauseIT {
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().getConfig().getCommonConfig().setSortBufferSize(128 *
1024);
+
EnvFactory.getEnv().getConfig().getCommonConfig().setMaxTsBlockSizeInByte(4 *
1024);
+ EnvFactory.getEnv().initClusterEnvironment();
+ prepareTableData(CREATE_SQLS);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void testScalarSubqueryAfterComparisonInOneTable() {
+ String sql;
+ String[] expectedHeader;
+ String[] retArray;
+
+ // Test case: s equals to the maximum value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s = (SELECT max(%s) from table1 WHERE device_id = 'd01')";
+ retArray = new String[] {"70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s not equals to the maximum value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s != ((SELECT max(%s) FROM table1 WHERE device_id = 'd01'))";
+ retArray = new String[] {"30,", "40,", "50,", "60,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s greater than the average value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s >= ((SELECT AVG(%s) FROM table1 WHERE device_id = 'd01'))";
+ retArray = new String[] {"50,", "60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s greater than the max value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s > ((SELECT max(%s) FROM table1 WHERE device_id = 'd01'))";
+ retArray = new String[] {};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s is less than the maximum value of s in table1 and greater
than the minimum value
+ // of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s < (SELECT max(%s) from table1 WHERE device_id = 'd01') and %s > (SELECT
min(%s) from table1 WHERE device_id = 'd01') ";
+ retArray = new String[] {"40,", "50,", "60,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(
+ sql, measurement, measurement, measurement, measurement,
measurement, measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s greater than the avg value of s in table1 and s5 = true
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s > ((SELECT avg(%s) FROM table1 WHERE device_id = 'd01' and s5 = true))";
+ retArray = new String[] {"60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s greater than the count value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s > (SELECT count(%s) FROM table1 WHERE device_id = 'd01')";
+ retArray = new String[] {"30,", "40,", "50,", "60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s less than the sum value of s in table1
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s < (SELECT sum(%s) FROM table1 WHERE device_id = 'd01')";
+ retArray = new String[] {"30,", "40,", "50,", "60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+ }
+
+ @Test
+ public void testScalarSubqueryAfterComparisonInDifferentTables() {
+ String sql;
+ String[] expectedHeader;
+ String[] retArray;
+
+ // Test case: s greater than the count value of s in table2
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s > (SELECT count(%s) from table2)";
+ retArray = new String[] {"30,", "40,", "50,", "60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: s less than the max value of s in table2 * the count value
of s in table2 * 10
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s < ((SELECT max(%s) from table2) * (SELECT count(%s) from table2)) * 10";
+ retArray = new String[] {"30,", "40,", "50,", "60,", "70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(sql, measurement, measurement, measurement,
measurement, measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+ }
+
+ @Test
+ public void testNestedScalarSubqueryAfterComparison() {
+ String sql;
+ String[] expectedHeader;
+ String[] retArray;
+
+ // Test case: nested scalar subquery in where clause
+ sql =
+ "SELECT cast(%s AS INT32) as %s FROM table1 WHERE device_id = 'd01'
and %s = (SELECT max(%s) from table1 where %s = (SELECT max(%s) from table1
WHERE device_id = 'd01'))";
+ retArray = new String[] {"70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(
+ sql, measurement, measurement, measurement, measurement,
measurement, measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+
+ // Test case: nested scalar subquery with table subquery
+ sql =
+ "SELECT %s from (SELECT cast(%s AS INT32) as %s FROM table1 WHERE
device_id = 'd01' and %s = (SELECT max(%s) from table1 where %s = (SELECT
max(%s) from table1 WHERE device_id = 'd01')))";
+ retArray = new String[] {"70,"};
+ for (String measurement : NUMERIC_MEASUREMENTS) {
+ expectedHeader = new String[] {measurement};
+ tableResultSetEqualTest(
+ String.format(
+ sql,
+ measurement,
+ measurement,
+ measurement,
+ measurement,
+ measurement,
+ measurement,
+ measurement),
+ expectedHeader,
+ retArray,
+ DATABASE_NAME);
+ }
+ }
+
+ @Test
+ public void testScalarSubqueryAfterComparisonLegalityCheck() {
+ // Legality check: subquery returns multiple rows (should fail)
+ tableAssertTestFail(
+ "select s1 from table1 where s1 = (select s1 from table1)",
+ "301: Scalar sub-query has returned multiple rows.",
+ DATABASE_NAME);
+
+ // Legality check: subquery can not be parsed
+ tableAssertTestFail(
+ "select s1 from table1 where s1 = (select s1 from)", "mismatched
input", DATABASE_NAME);
+
+ // Legality check: subquery can not be parsed(without parentheses)
+ tableAssertTestFail(
+ "select s1 from table1 where s1 = select s1 from table1",
+ "mismatched input",
+ DATABASE_NAME);
+
+ // Legality check: Main query can not be parsed
+ tableAssertTestFail(
+ "select s1 from table1 where s1 = (select max(s1) from table1) and",
+ "mismatched input",
+ DATABASE_NAME);
+ }
+}
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/SubqueryDataSetUtils.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/SubqueryDataSetUtils.java
new file mode 100644
index 00000000000..1bd779aac65
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/subquery/SubqueryDataSetUtils.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.relational.it.query.recent.subquery;
+
+public class SubqueryDataSetUtils {
+ protected static final String DATABASE_NAME = "subqueryTest";
+ protected static final String[] NUMERIC_MEASUREMENTS = new String[] {"s1",
"s2", "s3", "s4"};
+ protected static final String[] CREATE_SQLS =
+ new String[] {
+ "CREATE DATABASE " + DATABASE_NAME,
+ "USE " + DATABASE_NAME,
+ // table1
+ "CREATE TABLE table1(province STRING ID, city STRING ID, region STRING
ID, device_id STRING ID, color STRING ATTRIBUTE, type STRING ATTRIBUTE, s1
INT32 MEASUREMENT, s2 INT64 MEASUREMENT, s3 FLOAT MEASUREMENT, s4 DOUBLE
MEASUREMENT, s5 BOOLEAN MEASUREMENT, s6 TEXT MEASUREMENT, s7 STRING
MEASUREMENT, s8 BLOB MEASUREMENT, s9 TIMESTAMP MEASUREMENT, s10 DATE
MEASUREMENT)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
values
(2024-09-24T06:13:30.000+00:00,'shanghai','shanghai','huangpu','d01','red','A',30,30,30.0,30.0,true,'shanghai_huangpu_red_A_d01_30','shanghai_huangpu_red_A_d01_30',X'cafebabe30',2024-09-24T06:13:00.000+00:00,'2024-09-23')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
values
(2024-09-24T06:14:30.000+00:00,'shanghai','shanghai','huangpu','d01','red','A',40,40,40.0,40.0,false,'shanghai_huangpu_red_A_d01_40','shanghai_huangpu_red_A_d01_40',X'cafebabe40',2024-09-24T06:14:00.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
values
(2024-09-24T06:15:30.000+00:00,'shanghai','shanghai','huangpu','d01','red','A',50,50,50.0,50.0,true,'shanghai_huangpu_red_A_d01_50','shanghai_huangpu_red_A_d01_50',X'cafebabe50',2024-09-24T06:15:00.000+00:00,'2024-09-25')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
values
(2024-09-24T06:16:30.000+00:00,'shanghai','shanghai','huangpu','d01','red','A',60,60,60.0,60.0,false,'shanghai_huangpu_red_A_d01_60','shanghai_huangpu_red_A_d01_60',X'cafebabe60',2024-09-24T06:16:00.000+00:00,'2024-09-26')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10)
values
(2024-09-24T06:17:30.000+00:00,'shanghai','shanghai','huangpu','d01','red','A',70,70,70.0,70.0,true,'shanghai_huangpu_red_A_d01_70','shanghai_huangpu_red_A_d01_70',X'cafebabe70',2024-09-24T06:17:00.000+00:00,'2024-09-27')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s5,s6,s7,s9) values
(2024-09-24T06:15:36.000+00:00,'shanghai','shanghai','huangpu','d02','red','BBBBBBBBBBBBBBBB',36,true,'shanghai_huangpu_red_B_d02_36','shanghai_huangpu_red_B_d02_36',2024-09-24T06:15:36.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:40.000+00:00,'shanghai','shanghai','huangpu','d02','red','BBBBBBBBBBBBBBBB',40,40.0,'shanghai_huangpu_red_B_d02_40',2024-09-24T06:15:40.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s7,s8,s9) values
(2024-09-24T06:15:50.000+00:00,'shanghai','shanghai','huangpu','d02','red','BBBBBBBBBBBBBBBB',50000,'shanghai_huangpu_red_B_d02_50',X'cafebabe50',2024-09-24T06:15:50.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s8,s9) values
(2024-09-24T06:15:31.000+00:00,'shanghai','shanghai','huangpu','d03','yellow','A',31000,X'cafebabe31',2024-09-24T06:15:31.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:36.000+00:00,'shanghai','shanghai','huangpu','d03','yellow','A',36,36.0,'shanghai_huangpu_yellow_A_d03_36',2024-09-24T06:15:36.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s6,s8,s9) values
(2024-09-24T06:15:41.000+00:00,'shanghai','shanghai','huangpu','d03','yellow','A',41,41.0,false,'shanghai_huangpu_yellow_A_d03_41',X'cafebabe41',2024-09-24T06:15:41.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s4,s7,s9) values
(2024-09-24T06:15:46.000+00:00,'shanghai','shanghai','huangpu','d03','yellow','A',46000,46.0,'shanghai_huangpu_yellow_A_d03_46',2024-09-24T06:15:46.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s6,s9) values
(2024-09-24T06:15:51.000+00:00,'shanghai','shanghai','huangpu','d03','yellow','A',51.0,'shanghai_huangpu_yellow_A_d03_51',2024-09-24T06:15:51.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s5,s7,s9,s10) values
(2024-09-24T06:15:30.000+00:00,'shanghai','shanghai','huangpu','d04','yellow','BBBBBBBBBBBBBBBB',30.0,true,'shanghai_huangpu_yellow_B_d04_30',2024-09-24T06:15:30.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s9) values
(2024-09-24T06:15:40.000+00:00,'shanghai','shanghai','huangpu','d04','yellow','BBBBBBBBBBBBBBBB',40000,2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s6,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'shanghai','shanghai','huangpu','d04','yellow','BBBBBBBBBBBBBBBB',55,55.0,'shanghai_huangpu_yellow_B_d04_55',X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s6,s8,s9) values
(2024-09-24T06:15:30.000+00:00,'shanghai','shanghai','pudong','d05','red','A',30,30.0,'shanghai_pudong_red_A_d05_30',
X'cafebabe30',2024-09-24T06:15:30.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s3,s4,s6,s7,s9,s10)
values
(2024-09-24T06:15:35.000+00:00,'shanghai','shanghai','pudong','d05','red','A',35000,35.0,35.0,'shanghai_pudong_red_A_d05_35','shanghai_pudong_red_A_d05_35',2024-09-24T06:15:35.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s7,s9) values
(2024-09-24T06:15:40.000+00:00,'shanghai','shanghai','pudong','d05','red','A',40,40.0,true,'shanghai_pudong_red_A_d05_40',2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s5,s9,s10) values
(2024-09-24T06:15:50.000+00:00,'shanghai','shanghai','pudong','d05','red','A',50000,false,2024-09-24T06:15:50.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'shanghai','shanghai','pudong','d05','red','A',55,55.0,X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s5,s6,s7,s9) values
(2024-09-24T06:15:36.000+00:00,'shanghai','shanghai','pudong','d06','red','BBBBBBBBBBBBBBBB',36,true,'shanghai_pudong_red_B_d06_36','shanghai_pudong_red_B_d06_36',2024-09-24T06:15:36.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:40.000+00:00,'shanghai','shanghai','pudong','d06','red','BBBBBBBBBBBBBBBB',40,40.0,'shanghai_pudong_red_B_d06_40',2024-09-24T06:15:40.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s7,s8,s9) values
(2024-09-24T06:15:50.000+00:00,'shanghai','shanghai','pudong','d06','red','BBBBBBBBBBBBBBBB',50000,'shanghai_pudong_red_B_d06_50',X'cafebabe50',2024-09-24T06:15:50.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s8,s9) values
(2024-09-24T06:15:31.000+00:00,'shanghai','shanghai','pudong','d07','yellow','A',31000,X'cafebabe31',2024-09-24T06:15:31.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:36.000+00:00,'shanghai','shanghai','pudong','d07','yellow','A',36,36.0,'shanghai_pudong_yellow_A_d07_36',2024-09-24T06:15:36.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s6,s8,s9) values
(2024-09-24T06:15:41.000+00:00,'shanghai','shanghai','pudong','d07','yellow','A',41,41.0,false,'shanghai_pudong_yellow_A_d07_41',X'cafebabe41',2024-09-24T06:15:41.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s4,s7,s9) values
(2024-09-24T06:15:46.000+00:00,'shanghai','shanghai','pudong','d07','yellow','A',46000,46.0,'shanghai_pudong_yellow_A_d07_46',2024-09-24T06:15:46.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s6,s9) values
(2024-09-24T06:15:51.000+00:00,'shanghai','shanghai','pudong','d07','yellow','A',51.0,'shanghai_pudong_yellow_A_d07_51',2024-09-24T06:15:51.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s5,s7,s9,s10) values
(2024-09-24T06:15:30.000+00:00,'shanghai','shanghai','pudong','d08','yellow','BBBBBBBBBBBBBBBB',30.0,true,'shanghai_pudong_yellow_B_d08_30',2024-09-24T06:15:30.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s9) values
(2024-09-24T06:15:40.000+00:00,'shanghai','shanghai','pudong','d08','yellow','BBBBBBBBBBBBBBBB',40000,2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s6,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'shanghai','shanghai','pudong','d08','yellow','BBBBBBBBBBBBBBBB',55,55.0,'shanghai_pudong_yellow_B_d08_55',X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s6,s8,s9) values
(2024-09-24T06:15:30.000+00:00,'beijing','beijing','chaoyang','d09','red','A',30,30.0,'beijing_chaoyang_red_A_d09_30',
X'cafebabe30',2024-09-24T06:15:30.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s3,s4,s6,s7,s9,s10)
values
(2024-09-24T06:15:35.000+00:00,'beijing','beijing','chaoyang','d09','red','A',35000,35.0,35.0,'beijing_chaoyang_red_A_d09_35','beijing_chaoyang_red_A_d09_35',2024-09-24T06:15:35.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s7,s9) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','chaoyang','d09','red','A',40,40.0,true,'beijing_chaoyang_red_A_d09_40',2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s5,s9,s10) values
(2024-09-24T06:15:50.000+00:00,'beijing','beijing','chaoyang','d09','red','A',50000,false,2024-09-24T06:15:50.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'beijing','beijing','chaoyang','d09','red','A',55,55.0,X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s5,s6,s7,s9) values
(2024-09-24T06:15:36.000+00:00,'beijing','beijing','chaoyang','d10','red','BBBBBBBBBBBBBBBB',36,true,'beijing_chaoyang_red_B_d10_36','beijing_chaoyang_red_B_d10_36',2024-09-24T06:15:36.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','chaoyang','d10','red','BBBBBBBBBBBBBBBB',40,40.0,'beijing_chaoyang_red_B_d10_40',2024-09-24T06:15:40.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s7,s8,s9) values
(2024-09-24T06:15:50.000+00:00,'beijing','beijing','chaoyang','d10','red','BBBBBBBBBBBBBBBB',50000,'beijing_chaoyang_red_B_d10_50',X'cafebabe50',2024-09-24T06:15:50.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s8,s9) values
(2024-09-24T06:15:31.000+00:00,'beijing','beijing','chaoyang','d11','yellow','A',31000,X'cafebabe31',2024-09-24T06:15:31.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:36.000+00:00,'beijing','beijing','chaoyang','d11','yellow','A',36,36.0,'beijing_chaoyang_yellow_A_d11_36',2024-09-24T06:15:36.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s6,s8,s9) values
(2024-09-24T06:15:41.000+00:00,'beijing','beijing','chaoyang','d11','yellow','A',41,41.0,false,'beijing_chaoyang_yellow_A_d11_41',X'cafebabe41',2024-09-24T06:15:41.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s4,s7,s9) values
(2024-09-24T06:15:46.000+00:00,'beijing','beijing','chaoyang','d11','yellow','A',46000,46.0,'beijing_chaoyang_yellow_A_d11_46',2024-09-24T06:15:46.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s6,s9) values
(2024-09-24T06:15:51.000+00:00,'beijing','beijing','chaoyang','d11','yellow','A',51.0,'beijing_chaoyang_yellow_A_d11_51',2024-09-24T06:15:51.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s5,s7,s9,s10) values
(2024-09-24T06:15:30.000+00:00,'beijing','beijing','chaoyang','d12','yellow','BBBBBBBBBBBBBBBB',30.0,true,'beijing_chaoyang_yellow_B_d12_30',2024-09-24T06:15:30.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s9) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','chaoyang','d12','yellow','BBBBBBBBBBBBBBBB',40000,2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s6,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'beijing','beijing','chaoyang','d12','yellow','BBBBBBBBBBBBBBBB',55,55.0,'beijing_chaoyang_yellow_B_d12_55',X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s6,s8,s9) values
(2024-09-24T06:15:30.000+00:00,'beijing','beijing','haidian','d13','red','A',30,30.0,'beijing_haidian_red_A_d13_30',
X'cafebabe30',2024-09-24T06:15:30.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s3,s4,s6,s7,s9,s10)
values
(2024-09-24T06:15:35.000+00:00,'beijing','beijing','haidian','d13','red','A',35000,35.0,35.0,'beijing_haidian_red_A_d13_35','beijing_haidian_red_A_d13_35',2024-09-24T06:15:35.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s7,s9) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','haidian','d13','red','A',40,40.0,true,'beijing_haidian_red_A_d13_40',2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s5,s9,s10) values
(2024-09-24T06:15:50.000+00:00,'beijing','beijing','haidian','d13','red','A',50000,false,2024-09-24T06:15:50.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'beijing','beijing','haidian','d13','red','A',55,55.0,X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s5,s6,s7,s9) values
(2024-09-24T06:15:36.000+00:00,'beijing','beijing','haidian','d14','red','BBBBBBBBBBBBBBBB',36,true,'beijing_haidian_red_B_d14_36','beijing_haidian_red_B_d14_36',2024-09-24T06:15:36.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','haidian','d14','red','BBBBBBBBBBBBBBBB',40,40.0,'beijing_haidian_red_B_d14_40',2024-09-24T06:15:40.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s7,s8,s9) values
(2024-09-24T06:15:50.000+00:00,'beijing','beijing','haidian','d14','red','BBBBBBBBBBBBBBBB',50000,'beijing_haidian_red_B_d14_50',X'cafebabe50',2024-09-24T06:15:50.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s8,s9) values
(2024-09-24T06:15:31.000+00:00,'beijing','beijing','haidian','d15','yellow','A',31000,X'cafebabe31',2024-09-24T06:15:31.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s7,s9,s10) values
(2024-09-24T06:15:36.000+00:00,'beijing','beijing','haidian','d15','yellow','A',36,36.0,'beijing_haidian_yellow_A_d15_36',2024-09-24T06:15:36.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s3,s5,s6,s8,s9) values
(2024-09-24T06:15:41.000+00:00,'beijing','beijing','haidian','d15','yellow','A',41,41.0,false,'beijing_haidian_yellow_A_d15_41',X'cafebabe41',2024-09-24T06:15:41.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s4,s7,s9) values
(2024-09-24T06:15:46.000+00:00,'beijing','beijing','haidian','d15','yellow','A',46000,46.0,'beijing_haidian_yellow_A_d15_46',2024-09-24T06:15:46.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s6,s9) values
(2024-09-24T06:15:51.000+00:00,'beijing','beijing','haidian','d15','yellow','A',51.0,'beijing_haidian_yellow_A_d15_51',2024-09-24T06:15:51.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s3,s5,s7,s9,s10) values
(2024-09-24T06:15:30.000+00:00,'beijing','beijing','haidian','d16','yellow','BBBBBBBBBBBBBBBB',30.0,true,'beijing_haidian_yellow_B_d16_30',2024-09-24T06:15:30.000+00:00,'2024-09-24')",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s2,s9) values
(2024-09-24T06:15:40.000+00:00,'beijing','beijing','haidian','d16','yellow','BBBBBBBBBBBBBBBB',40000,2024-09-24T06:15:40.000+00:00)",
+ "INSERT INTO
table1(time,province,city,region,device_id,color,type,s1,s4,s6,s8,s9) values
(2024-09-24T06:15:55.000+00:00,'beijing','beijing','haidian','d16','yellow','BBBBBBBBBBBBBBBB',55,55.0,'beijing_haidian_yellow_B_d16_55',X'cafebabe55',2024-09-24T06:15:55.000+00:00)",
+ // table2
+ "CREATE TABLE table2(device_id STRING ID, s1 INT32 MEASUREMENT, s2
INT64 MEASUREMENT, s3 FLOAT MEASUREMENT, s4 DOUBLE MEASUREMENT, s5 BOOLEAN
MEASUREMENT, s6 TEXT MEASUREMENT, s7 STRING MEASUREMENT, s8 BLOB MEASUREMENT,
s9 TIMESTAMP MEASUREMENT, s10 DATE MEASUREMENT)",
+ "INSERT INTO table2(time,device_id,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10) "
+ + " values(1, 'd1', 1, 11, 1.1, 11.1, true, 'text1', 'string1',
X'cafebabe01', 1, '2024-10-01')",
+ "INSERT INTO table2(time,device_id,s1,s2,s3,s4,s5) "
+ + " values(2, 'd1', 2, 22, 2.2, 22.2, false)",
+ "INSERT INTO table2(time,device_id,s6,s7,s8,s9,s10) "
+ + " values(3, 'd1', 'text3', 'string3', X'cafebabe03', 3,
'2024-10-03')",
+ "INSERT INTO table2(time,device_id,s6,s7,s8,s9,s10) "
+ + " values(4, 'd1', 'text4', 'string4', X'cafebabe04', 4,
'2024-10-04')",
+ "INSERT INTO table2(time,device_id,s1,s2,s3,s4,s5) "
+ + " values(5, 'd1', 5, 55, 5.5, 55.5, false)",
+ "FLUSH",
+ "CLEAR ATTRIBUTE CACHE",
+ };
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/EnforceSingleRowOperator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/EnforceSingleRowOperator.java
new file mode 100644
index 00000000000..891612f5966
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/EnforceSingleRowOperator.java
@@ -0,0 +1,115 @@
+/*
+ * 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.queryengine.execution.operator.process;
+
+import org.apache.iotdb.db.queryengine.execution.MemoryEstimationHelper;
+import org.apache.iotdb.db.queryengine.execution.operator.Operator;
+import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import org.apache.tsfile.common.conf.TSFileDescriptor;
+import org.apache.tsfile.read.common.block.TsBlock;
+import org.apache.tsfile.utils.RamUsageEstimator;
+
+public class EnforceSingleRowOperator implements ProcessOperator {
+ private static final long INSTANCE_SIZE =
+ RamUsageEstimator.shallowSizeOfInstance(EnforceSingleRowOperator.class);
+
+ private static final String ERROR_MESSAGE = "Scalar sub-query has returned
multiple rows.";
+
+ private final OperatorContext operatorContext;
+ private final Operator child;
+
+ private boolean finished = false;
+
+ public EnforceSingleRowOperator(OperatorContext operatorContext, Operator
child) {
+ this.operatorContext = operatorContext;
+ this.child = child;
+ }
+
+ @Override
+ public ListenableFuture<?> isBlocked() {
+ return child.isBlocked();
+ }
+
+ @Override
+ public TsBlock next() throws Exception {
+ if (finished) {
+ throw new IllegalStateException(ERROR_MESSAGE);
+ }
+ TsBlock tsBlock = child.next();
+ if (tsBlock != null && tsBlock.getPositionCount() > 1) {
+ throw new IllegalStateException(ERROR_MESSAGE);
+ }
+ if (tsBlock != null) {
+ finished = true;
+ }
+ return tsBlock;
+ }
+
+ @Override
+ public boolean hasNext() throws Exception {
+ boolean hasNext = child.hasNext();
+ if (finished && hasNext) {
+ throw new IllegalStateException(ERROR_MESSAGE);
+ }
+ return hasNext;
+ }
+
+ @Override
+ public void close() throws Exception {
+ if (child != null) {
+ child.close();
+ }
+ }
+
+ @Override
+ public boolean isFinished() throws Exception {
+ return finished || child.isFinished();
+ }
+
+ @Override
+ public long calculateMaxPeekMemory() {
+ return child.calculateMaxPeekMemory();
+ }
+
+ @Override
+ public long calculateMaxReturnSize() {
+ return child.calculateMaxReturnSize()
+ / TSFileDescriptor.getInstance().getConfig().getMaxTsBlockLineNumber();
+ }
+
+ @Override
+ public long calculateRetainedSizeAfterCallingNext() {
+ return child.calculateRetainedSizeAfterCallingNext();
+ }
+
+ @Override
+ public long ramBytesUsed() {
+ return INSTANCE_SIZE
+ + MemoryEstimationHelper.getEstimatedSizeOfAccountableObject(child)
+ +
MemoryEstimationHelper.getEstimatedSizeOfAccountableObject(operatorContext);
+ }
+
+ @Override
+ public OperatorContext getOperatorContext() {
+ return operatorContext;
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
index c41063943c0..5941950d497 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
@@ -37,6 +37,7 @@ import
org.apache.iotdb.db.queryengine.execution.exchange.source.ISourceHandle;
import org.apache.iotdb.db.queryengine.execution.operator.Operator;
import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext;
import
org.apache.iotdb.db.queryengine.execution.operator.process.CollectOperator;
+import
org.apache.iotdb.db.queryengine.execution.operator.process.EnforceSingleRowOperator;
import
org.apache.iotdb.db.queryengine.execution.operator.process.FilterAndProjectOperator;
import
org.apache.iotdb.db.queryengine.execution.operator.process.LimitOperator;
import
org.apache.iotdb.db.queryengine.execution.operator.process.OffsetOperator;
@@ -110,6 +111,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.planner.Symbol;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.CollectNode;
+import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.EnforceSingleRowNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.GapFillNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.JoinNode;
@@ -176,6 +178,7 @@ import java.util.stream.Collectors;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static java.util.Objects.requireNonNull;
+import static org.apache.iotdb.commons.conf.IoTDBConstant.TIME;
import static
org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory.MEASUREMENT;
import static
org.apache.iotdb.db.queryengine.common.DataNodeEndPoints.isSameNode;
import static
org.apache.iotdb.db.queryengine.execution.operator.process.join.merge.MergeSortComparator.getComparatorForTable;
@@ -1189,6 +1192,7 @@ public class TableOperatorGenerator extends
PlanVisitor<Operator, LocalExecution
rightOutputSymbolIdx[i] =
node.getRightChild().getOutputSymbols().indexOf(node.getRightOutputSymbols().get(i));
}
+
// cross join does not need time column
if (node.isCrossJoin()) {
return new SimpleNestedLoopCrossJoinOperator(
@@ -1231,6 +1235,21 @@ public class TableOperatorGenerator extends
PlanVisitor<Operator, LocalExecution
throw new IllegalStateException("Unsupported join type: " +
node.getJoinType());
}
+ @Override
+ public Operator visitEnforceSingleRow(
+ EnforceSingleRowNode node, LocalExecutionPlanContext context) {
+ Operator child = node.getChild().accept(this, context);
+ OperatorContext operatorContext =
+ context
+ .getDriverContext()
+ .addOperatorContext(
+ context.getNextOperatorId(),
+ node.getPlanNodeId(),
+ LimitOperator.class.getSimpleName());
+
+ return new EnforceSingleRowOperator(operatorContext, child);
+ }
+
@Override
public Operator visitCountMerge(
final CountSchemaMergeNode node, final LocalExecutionPlanContext
context) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
index 884508534e0..3f1a08f1081 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
@@ -36,6 +36,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.AggregationTableScanNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.CollectNode;
+import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.EnforceSingleRowNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FillNode;
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.node.GapFillNode;
@@ -620,6 +621,18 @@ public class TableDistributedPlanGenerator
return resultTableScanNodeList;
}
+ @Override
+ public List<PlanNode> visitEnforceSingleRow(EnforceSingleRowNode node,
PlanContext context) {
+ List<PlanNode> childrenNodes = node.getChild().accept(this, context);
+ OrderingScheme childOrdering =
nodeOrderingMap.get(childrenNodes.get(0).getPlanNodeId());
+ if (childOrdering != null) {
+ nodeOrderingMap.put(node.getPlanNodeId(), childOrdering);
+ }
+
+ node.setChild(mergeChildrenViaCollectOrMergeSort(childOrdering,
childrenNodes));
+ return Collections.singletonList(node);
+ }
+
private void buildRegionNodeMap(
AggregationTableScanNode originalAggTableScanNode,
List<List<TRegionReplicaSet>> regionReplicaSetsList,
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
index a2166e1a1ca..25f5cbf2159 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/PushPredicateIntoTableScan.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations;
import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.partition.DataPartition;
import org.apache.iotdb.commons.partition.DataPartitionQueryParam;
import org.apache.iotdb.db.conf.IoTDBConfig;
@@ -585,10 +586,7 @@ public class PushPredicateIntoTableScan implements
PlanOptimizer {
List<JoinNode.EquiJoinClause> equiJoinClauses = new ArrayList<>();
ImmutableList.Builder<Expression> joinFilterBuilder =
ImmutableList.builder();
for (Expression conjunct : extractConjuncts(newJoinPredicate)) {
- if (joinEqualityExpression(
- conjunct,
- node.getLeftChild().getOutputSymbols(),
- node.getRightChild().getOutputSymbols())) {
+ if (joinEqualityExpressionOnTimeColumn(conjunct, node)) {
ComparisonExpression equality = (ComparisonExpression) conjunct;
boolean alignedComparison =
@@ -724,6 +722,24 @@ public class PushPredicateIntoTableScan implements
PlanOptimizer {
return output;
}
+ private boolean joinEqualityExpressionOnTimeColumn(Expression conjunct,
JoinNode node) {
+ if (!joinEqualityExpression(
+ conjunct,
+ node.getLeftChild().getOutputSymbols(),
+ node.getRightChild().getOutputSymbols())) {
+ return false;
+ }
+ // conjunct must be a comparison expression
+ ComparisonExpression equality = (ComparisonExpression) conjunct;
+ // After Optimization, some subqueries are transformed into Join.
+ // For now, we only support join on time, so we need to use
FilterOperator + CrossJoinOperator
+ // to simulate the join with equality criteria on columns other than
time column.
+ // todo: after supporting join on other columns, we need to remove the
following code, this is
+ // temporary walkaround.
+ return IoTDBConstant.TIME.equalsIgnoreCase(equality.getLeft().toString())
+ ||
IoTDBConstant.TIME.equalsIgnoreCase(equality.getRight().toString());
+ }
+
private Symbol symbolForExpression(Expression expression) {
if (expression instanceof SymbolReference) {
return Symbol.from(expression);