From d20f00ce244e99327941949b406b166db987a931 Mon Sep 17 00:00:00 2001 From: FearfulTomcat27 <1471335448@qq.com> Date: Thu, 23 Jan 2025 00:56:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0Greatest=20=E5=92=8C?= =?UTF-8?q?=20Least=20=E7=9A=84=20IT=EF=BC=8C=E5=B9=B6=E6=94=AF=E6=8C=81?= =?UTF-8?q?=20Boolean=20=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scalar/IoTDBFormatFunctionTableIT.java | 16 +- .../old/query/IoTDBGreatestLeastTableIT.java | 233 ++++++++++++++++++ .../metadata/TableMetadataImpl.java | 1 - ...bstractGreatestLeastColumnTransformer.java | 10 +- .../multi/GreatestColumnTransformer.java | 7 +- .../column/multi/LeastColumnTransformer.java | 7 +- 6 files changed, 253 insertions(+), 21 deletions(-) create mode 100644 integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBGreatestLeastTableIT.java diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBFormatFunctionTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBFormatFunctionTableIT.java index 0ed90703ec24..4a4c1414d3f2 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBFormatFunctionTableIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/builtinfunction/scalar/IoTDBFormatFunctionTableIT.java @@ -49,12 +49,12 @@ public class IoTDBFormatFunctionTableIT { // normal cases "CREATE DATABASE " + DATABASE_NAME, "USE " + DATABASE_NAME, - "CREATE TABLE number_table(device_id STRING ID, s1 INT32 MEASUREMENT, s2 INT64 MEASUREMENT, s3 FLOAT MEASUREMENT, s4 DOUBLE MEASUREMENT)", - "CREATE TABLE string_table(device_id STRING ID, s1 STRING MEASUREMENT, s2 TEXT MEASUREMENT)", - "CREATE TABLE boolean_table(device_id STRING ID, s1 BOOLEAN MEASUREMENT)", - "CREATE TABLE timestamp_table(device_id STRING ID, s1 TIMESTAMP MEASUREMENT)", - "CREATE TABLE date_table(device_id STRING ID, s1 DATE MEASUREMENT)", - "CREATE TABLE null_table(device_id STRING ID, s1 INT32 MEASUREMENT)", + "CREATE TABLE number_table(device_id STRING TAG, s1 INT32 FIELD, s2 INT64 FIELD, s3 FLOAT FIELD, s4 DOUBLE FIELD)", + "CREATE TABLE string_table(device_id STRING TAG, s1 STRING FIELD, s2 TEXT FIELD)", + "CREATE TABLE boolean_table(device_id STRING TAG, s1 BOOLEAN FIELD)", + "CREATE TABLE timestamp_table(device_id STRING TAG, s1 TIMESTAMP FIELD)", + "CREATE TABLE date_table(device_id STRING TAG, s1 DATE FIELD)", + "CREATE TABLE null_table(device_id STRING TAG, s1 INT32 FIELD)", // data for number series "INSERT INTO number_table(time, device_id, s1, s2, s3, s4) VALUES (10, 'd1', 1000000, 1000000, 3.1415926, 3.1415926)", "INSERT INTO number_table(time, device_id, s1, s2, s3, s4) VALUES (20, 'd1', 1, 1, 1234567.25, 1234567.25)", @@ -205,9 +205,5 @@ public void testAnomalies() { "SELECT FORMAT('%s') FROM string_table", "701: Scalar function format must have at least two arguments, and first argument must be char type.", DATABASE_NAME); - - tableAssertTestFail( - "SELECT FORMAT('%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS.%1$tL',s1) FROM date_table", - "701:", DATABASE_NAME); } } diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBGreatestLeastTableIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBGreatestLeastTableIT.java new file mode 100644 index 000000000000..2bac3d90e0a2 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/old/query/IoTDBGreatestLeastTableIT.java @@ -0,0 +1,233 @@ +/* + * Licensed 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.old.query; + +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.apache.iotdb.itbase.env.BaseEnv; + +import org.junit.AfterClass; +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.Statement; + +import static org.apache.iotdb.db.it.utils.TestUtils.tableAssertTestFail; +import static org.apache.iotdb.db.it.utils.TestUtils.tableResultSetEqualTest; + +@RunWith(IoTDBTestRunner.class) +@Category({TableLocalStandaloneIT.class, TableClusterIT.class}) +public class IoTDBGreatestLeastTableIT { + + private static final String DATABASE_NAME = "db"; + + private static final String[] SQLs = + new String[] { + "CREATE DATABASE " + DATABASE_NAME, + "USE " + DATABASE_NAME, + "CREATE TABLE boolean_table(device_id STRING TAG, bool1 BOOLEAN FIELD, bool2 BOOLEAN FIELD)", + "CREATE TABLE number_table(device_id STRING TAG, int1 INT32 FIELD, int2 INT32 FIELD, long1 INT64 FIELD, long2 INT64 FIELD, float1 FLOAT FIELD, float2 FLOAT FIELD, double1 DOUBLE FIELD, double2 DOUBLE FIELD)", + "CREATE TABLE string_table(device_id STRING TAG, string1 STRING FIELD, string2 STRING FIELD, text1 TEXT FIELD, text2 TEXT FIELD)", + "CREATE TABLE mix_type_table(device_id STRING TAG, s1 INT32 FIELD, s2 INT64 FIELD, s3 FLOAT FIELD, s4 DOUBLE FIELD, s5 BOOLEAN FIELD, s6 STRING FIELD, s7 TEXT FIELD)", + "CREATE TABLE null_table(device_id STRING TAG, s1 INT32 FIELD, s2 INT32 FIELD)", + "CREATE TABLE any_null_table(device_id STRING TAG, s1 INT32 FIELD, s2 INT32 FIELD)", + // normal case + "INSERT INTO number_table(time, device_id, int1, int2, long1, long2, float1, float2, double1, double2) VALUES (10, 'd1', 1000000, 2000000, 1000000, 2000000, 10.1, 20.2, 10.1, 20.2)", + "INSERT INTO string_table(time, device_id, string1, string2, text1, text2) VALUES(10, 'd1', 'aaa', 'bbb', 'aaa', 'bbb')", + "INSERT INTO boolean_table(time, device_id, bool1, bool2) VALUES(10, 'd1', true, false)", + "INSERT INTO mix_type_table(time, device_id, s1, s2, s3, s4, s5, s6, s7) VALUES(10, 'd1', 1, 1, 1.0, 1.0, true, 'a', 'a')", + "INSERT INTO null_table(time, device_id) VALUES(10, 'd1')", + "INSERT INTO any_null_table(time, device_id, s1) VALUES(10, 'd1', 1)" + }; + + @BeforeClass + public static void setUp() throws Exception { + EnvFactory.getEnv().initClusterEnvironment(); + insertData(); + } + + protected static void insertData() { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + + for (String sql : SQLs) { + statement.execute(sql); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @AfterClass + public static void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void testNumberTypeGreatestFunction() { + + tableResultSetEqualTest( + "SELECT GREATEST(int1, int2) FROM number_table", + new String[] {"_col0"}, + new String[] {"2000000,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT GREATEST(long1, long2) FROM number_table", + new String[] {"_col0"}, + new String[] {"2000000,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT GREATEST(float1, float2) FROM number_table", + new String[] {"_col0"}, + new String[] {"20.2,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT GREATEST(double1, double2) FROM number_table", + new String[] {"_col0"}, + new String[] {"20.2,"}, + DATABASE_NAME); + } + + @Test + public void testNumberTypeLeastFunction() { + tableResultSetEqualTest( + "SELECT LEAST(int1, int2) FROM number_table", + new String[] {"_col0"}, + new String[] {"1000000,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(long1, long2) FROM number_table", + new String[] {"_col0"}, + new String[] {"1000000,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(float1, float2) FROM number_table", + new String[] {"_col0"}, + new String[] {"10.1,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(double1, double2) FROM number_table", + new String[] {"_col0"}, + new String[] {"10.1,"}, + DATABASE_NAME); + } + + @Test + public void testStringTypeGreatestFunction() { + tableResultSetEqualTest( + "SELECT GREATEST(string1, string2) FROM string_table", + new String[] {"_col0"}, + new String[] {"bbb,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT GREATEST(text1, text2) FROM string_table", + new String[] {"_col0"}, + new String[] {"bbb,"}, + DATABASE_NAME); + } + + @Test + public void testStringTypeLeastFunction() { + tableResultSetEqualTest( + "SELECT LEAST(string1, string2) FROM string_table", + new String[] {"_col0"}, + new String[] {"aaa,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(text1, text2) FROM string_table", + new String[] {"_col0"}, + new String[] {"aaa,"}, + DATABASE_NAME); + } + + @Test + public void testBooleanTypeGreatestFunction() { + tableResultSetEqualTest( + "SELECT GREATEST(bool1, bool2) FROM boolean_table", + new String[] {"_col0"}, + new String[] {"true,"}, + DATABASE_NAME); + } + + @Test + public void testBooleanTypeLeastFunction() { + tableResultSetEqualTest( + "SELECT LEAST(bool1, bool2) FROM boolean_table", + new String[] {"_col0"}, + new String[] {"false,"}, + DATABASE_NAME); + } + + @Test + public void testAllNullValue() { + tableResultSetEqualTest( + "SELECT GREATEST(s1, s2) FROM null_table", + new String[] {"_col0"}, + new String[] {"null,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(s1, s2) FROM null_table", + new String[] {"_col0"}, + new String[] {"null,"}, + DATABASE_NAME); + } + + @Test + public void testAnyNullValue() { + tableResultSetEqualTest( + "SELECT GREATEST(s1, s2) FROM any_null_table", + new String[] {"_col0"}, + new String[] {"1,"}, + DATABASE_NAME); + + tableResultSetEqualTest( + "SELECT LEAST(s1, s2) FROM any_null_table", + new String[] {"_col0"}, + new String[] {"1,"}, + DATABASE_NAME); + } + + @Test + public void testAnomalies() { + // do not support different type + for (int i = 1; i <= 7; i++) { + for (int j = i + 1; j <= 7; j++) { + tableAssertTestFail( + String.format("SELECT LEAST(s%d, s%d) FROM mix_type_table", i, j), + "701: Scalar function least must have at least two arguments, and all type must be the same.", + DATABASE_NAME); + + tableAssertTestFail( + String.format("SELECT GREATEST(s%d, s%d) FROM mix_type_table", i, j), + "701: Scalar function greatest must have at least two arguments, and all type must be the same.", + DATABASE_NAME); + } + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java index ca3a227b458b..ae4b46569c16 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/TableMetadataImpl.java @@ -888,7 +888,6 @@ public static boolean isTwoTypeComparable(List argumentTypes) { } // Boolean type and Binary Type can not be compared with other types - // Unknown type can compare with other types return (isNumericType(left) && isNumericType(right)) || (isCharType(left) && isCharType(right)) || (isUnknownType(left) && (isNumericType(right) || isCharType(right))) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java index 36fd4c805e6f..40dd6bfe1b5c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/AbstractGreatestLeastColumnTransformer.java @@ -14,9 +14,6 @@ package org.apache.iotdb.db.queryengine.transformation.dag.column.multi; -import org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager; -import org.apache.iotdb.db.queryengine.plan.relational.type.TypeManager; -import org.apache.iotdb.db.queryengine.plan.relational.utils.TypeUtil; import org.apache.iotdb.db.queryengine.transformation.dag.column.ColumnTransformer; import org.apache.tsfile.block.column.Column; @@ -26,8 +23,6 @@ import org.apache.tsfile.utils.Binary; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; import java.util.List; public abstract class AbstractGreatestLeastColumnTransformer extends MultiColumnTransformer { @@ -66,6 +61,9 @@ protected void doTransform( protected void transform(ColumnBuilder builder, List childrenColumns, int i) { TypeEnum type = columnTransformerList.get(0).getType().getTypeEnum(); switch (type) { + case BOOLEAN: + transformBoolean(builder, getValues(childrenColumns, i, Column::getBoolean)); + break; case INT32: case DATE: transformInt(builder, getValues(childrenColumns, i, Column::getInt)); @@ -105,6 +103,8 @@ private interface ValueExtractor { T extract(Column column, int index); } + protected abstract void transformBoolean(ColumnBuilder builder, List values); + protected abstract void transformInt(ColumnBuilder builder, List values); protected abstract void transformLong(ColumnBuilder builder, List values); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/GreatestColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/GreatestColumnTransformer.java index f2aa4a368877..6b288ab5920c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/GreatestColumnTransformer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/GreatestColumnTransformer.java @@ -21,8 +21,6 @@ import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BytesUtils; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class GreatestColumnTransformer extends AbstractGreatestLeastColumnTransformer { @@ -30,6 +28,11 @@ public GreatestColumnTransformer(Type returnType, List column super(returnType, columnTransformerList); } + @Override + protected void transformBoolean(ColumnBuilder builder, List values) { + returnType.writeBoolean(builder, values.stream().max(Boolean::compareTo).orElse(false)); + } + @Override protected void transformInt(ColumnBuilder builder, List values) { returnType.writeInt(builder, values.stream().max(Integer::compareTo).orElse(0)); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/LeastColumnTransformer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/LeastColumnTransformer.java index 1e6e0bed77d8..9028906acb90 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/LeastColumnTransformer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/transformation/dag/column/multi/LeastColumnTransformer.java @@ -21,8 +21,6 @@ import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.BytesUtils; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; public class LeastColumnTransformer extends AbstractGreatestLeastColumnTransformer { @@ -30,7 +28,10 @@ public LeastColumnTransformer(Type returnType, List columnTra super(returnType, columnTransformerList); } - // values 都不为空,已在前面进行校验 + @Override + protected void transformBoolean(ColumnBuilder builder, List values) { + returnType.writeBoolean(builder, values.stream().min(Boolean::compareTo).orElse(false)); + } @Override protected void transformInt(ColumnBuilder builder, List values) {