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 d50f5d7e509 Support IF expression in Table model
d50f5d7e509 is described below
commit d50f5d7e509b18c17ba730e87549d1d40b0af526
Author: waynextz <[email protected]>
AuthorDate: Thu Jul 31 15:13:39 2025 +0800
Support IF expression in Table model
---
.../it/db/it/IoTDBIfExpressionTableIT.java | 384 +++++++++++++++++++++
1 file changed, 384 insertions(+)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBIfExpressionTableIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBIfExpressionTableIT.java
new file mode 100644
index 00000000000..7f512283c69
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/db/it/IoTDBIfExpressionTableIT.java
@@ -0,0 +1,384 @@
+/*
+ * 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.db.it;
+
+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 java.util.ArrayList;
+import java.util.List;
+
+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;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBIfExpressionTableIT {
+ private static final String DATABASE = "testDatabase";
+
+ private static final String[] expectedHeader = {"_col0"};
+
+ private static final String[] SQLs =
+ new String[] {
+ // normal cases
+ "CREATE DATABASE " + DATABASE,
+ "Use " + DATABASE,
+ "CREATE table table1 (device_id STRING TAG, s1 INT32 FIELD, s5 BOOLEAN
FIELD, s6 TEXT FIELD, s7 INT64 FIELD)",
+ "CREATE table table2 (device_id STRING TAG, s3 FLOAT FIELD, s4 DOUBLE
FIELD)",
+ "CREATE table table3 (device_id STRING TAG, s2 INT64 FIELD)",
+ "INSERT INTO table1(time, device_id, s1, s6) values(100, 'd1', 0,
'text100')",
+ "INSERT INTO table1(time, device_id, s1, s6) values(200, 'd1', 11,
'text200')",
+ "INSERT INTO table1(time, device_id, s1, s6) values(300, 'd1', 22,
'text300')",
+ "INSERT INTO table1(time, device_id, s1, s6) values(400, 'd1', 33,
'text400')",
+ "INSERT INTO table2(time, device_id, s3) values(100, 'd1', 0)",
+ "INSERT INTO table2(time, device_id, s3) values(200, 'd1', 11)",
+ "INSERT INTO table2(time, device_id, s3) values(300, 'd1', 22)",
+ "INSERT INTO table2(time, device_id, s3) values(400, 'd1', 33)",
+ "INSERT INTO table2(time, device_id, s4) values(100, 'd1', 44)",
+ "INSERT INTO table2(time, device_id, s4) values(200, 'd1', 55)",
+ "INSERT INTO table2(time, device_id, s4) values(300, 'd1', 66)",
+ "INSERT INTO table2(time, device_id, s4) values(400, 'd1', 77)",
+ };
+
+ @BeforeClass
+ public static void setUp() throws Exception {
+ EnvFactory.getEnv().initClusterEnvironment();
+ prepareTableData(SQLs);
+ List<String> moreSQLs = new ArrayList<>();
+ moreSQLs.add("use " + DATABASE);
+ for (int i = 0; i < 100; i++) {
+ moreSQLs.add(
+ String.format("INSERT INTO table3(time,device_id,s2) values(%d,
'd1', %d)", i, i));
+ }
+ prepareTableData(moreSQLs);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ }
+
+ @Test
+ public void testKind1Basic() {
+ String[] retArray = new String[] {"99,", "9999,", "9999,", "999,"};
+ tableResultSetEqualTest(
+ "select if(s1=0, 99, if(s1>22, 999, 9999)) from table1",
+ expectedHeader,
+ retArray,
+ DATABASE);
+
+ // without false value
+ retArray = new String[] {"99,", "null,", "null,", "999,"};
+ tableResultSetEqualTest(
+ "select if(s1=0, 99, if(s1>22, 999)) from table1", expectedHeader,
retArray, DATABASE);
+ }
+
+ @Test
+ public void testKind2Basic() {
+ String sql = "select if(s3=0.0, s4, if(s3=22.0, 999.0, 9999.0)) from
table2";
+ String[] retArray = new String[] {"44.0,", "9999.0,", "999.0,", "9999.0,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // without false value
+ sql = "select if(s3=0.0, s4, if(s3=22.0, 999.0)) from table2";
+ retArray = new String[] {"44.0,", "null,", "999.0,", "null,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+ }
+
+ @Test
+ public void testShortCircuitEvaluation() {
+ String[] retArray = new String[] {"0,", "11,", "22,", "33,"};
+ tableResultSetEqualTest(
+ "select if(1=0, s1/0, if(1!=0, s1)) from table1", expectedHeader,
retArray, DATABASE);
+ }
+
+ @Test
+ public void testKind1InputTypeRestrict() {
+ // IF condition must return BOOLEAN
+ String sql = "select if(s1+1, 20, 22) from table1";
+ String msg = "701: IF condition must evaluate to a BOOLEAN (actual:
INT32)";
+ tableAssertTestFail(sql, msg, DATABASE);
+ }
+
+ @Test
+ public void testKind2InputTypeRestrict() {
+ // the expression in IF condition must be able to be equated with the
expression in IF condition
+ String sql = "select if(s1='1', 20, 22) from table1";
+ String msg = "701: Cannot apply operator: INT32 = STRING";
+ tableAssertTestFail(sql, msg, DATABASE);
+ }
+
+ @Test
+ public void testKind1OutputTypeRestrict() {
+ // BOOLEAN and other types cannot exist at the same time
+ String[] retArray = new String[] {"true,", "false,", "true,", "true,"};
+ // success
+ tableResultSetEqualTest(
+ "select if(s1<=0, true, if(s1=11, false, true)) from table1",
+ expectedHeader,
+ retArray,
+ DATABASE);
+ // fail
+ tableAssertTestFail(
+ "select if(s1<=0, true, 22) from table1",
+ "701: Result types for IF must be the same: BOOLEAN vs INT32",
+ DATABASE);
+
+ // INT32 and INT64 cases
+ retArray = new String[] {"0,", "100,", "22,", "33,"};
+ // success for int32 value and int32 filed
+ tableResultSetEqualTest(
+ "select if(s1=11, 100, s1) from table1", expectedHeader, retArray,
DATABASE);
+
+ // fail for int32 value and int64 filed
+ tableAssertTestFail(
+ "select if(s1=11, 100, s7) from table1",
+ "701: Result types for IF must be the same: INT32 vs INT64",
+ DATABASE);
+
+ // TEXT and other types cannot exist at the same time
+ retArray = new String[] {"good,", "bad,", "okk,", "okk,"};
+ // success
+ tableResultSetEqualTest(
+ "select if(s1<=0, 'good', if(s1=11, 'bad', 'okk')) from table1",
+ expectedHeader,
+ retArray,
+ DATABASE);
+ // fail
+ tableAssertTestFail(
+ "select if(s1<=0, 'good', 22) from table1",
+ "701: Result types for IF must be the same: STRING vs INT32",
+ DATABASE);
+
+ // 4 numerical types(INT LONG FLOAT DOUBLE) cases
+ // fail for int32 and int64 values
+ tableAssertTestFail(
+ "select if(s1=0, 99, " + Long.MAX_VALUE + ") from table1",
+ "701: Result types for IF must be the same: INT32 vs INT64",
+ DATABASE);
+
+ // fail for int32 and int64 fields
+ tableAssertTestFail(
+ "select if(s1=11, s1, s7) from table1",
+ "701: Result types for IF must be the same: INT32 vs INT64",
+ DATABASE);
+
+ // success for float and double values
+ String maxValueOfDouble = Double.MAX_VALUE + ",";
+ retArray = new String[] {"1.2,", maxValueOfDouble, maxValueOfDouble,
maxValueOfDouble};
+ tableResultSetEqualTest(
+ "select if(s1=0, 1.2, " + Double.MAX_VALUE + ") from table1",
+ expectedHeader,
+ retArray,
+ DATABASE);
+
+ // fail for float and double fields
+ tableAssertTestFail(
+ "select if(s3=11, s3, s4) from table2",
+ "701: Result types for IF must be the same: FLOAT vs DOUBLE",
+ DATABASE);
+
+ // fail for int32 and float/double
+ tableAssertTestFail(
+ "select if(s1=0, 99, 1.2) from table1",
+ "701: Result types for IF must be the same: INT32 vs DOUBLE",
+ DATABASE);
+
+ // fail for int32, int64, float and double
+ tableAssertTestFail(
+ "select if(s1=0, 99, if(s1=11, 99.9, if(s1=22, 8589934588, if(s1=33,
999.9999999999, "
+ + Long.MAX_VALUE
+ + ")))) from table1",
+ "701: Result types for IF must be the same: DOUBLE vs INT64",
+ DATABASE);
+ }
+
+ @Test
+ public void testKind1LargeNumberBranches() {
+ StringBuilder sqlBuilder = new StringBuilder();
+ List<String> retList = new ArrayList<>();
+ sqlBuilder.append("select ");
+ for (int i = 0; i < 100; i++) {
+ sqlBuilder.append(String.format("if(s2=%d, s2*%d ", i, i * 100));
+ if (i != 99) {
+ sqlBuilder.append(", ");
+ }
+ retList.add(String.format("%d,", i * i * 100));
+ }
+ for (int i = 0; i < 100; i++) {
+ sqlBuilder.append(")");
+ }
+ sqlBuilder.append(" from table3");
+ tableResultSetEqualTest(
+ sqlBuilder.toString(), expectedHeader, retList.toArray(new String[]
{}), DATABASE);
+ }
+
+ @Test
+ public void testKind1UsedInOtherOperation() {
+ String sql;
+ String[] retArray;
+
+ // use in scalar operation
+ // multiply
+ sql = "select 2 * if(s1=0, 99, if(s1=22.0, 999, 9999)) from table1";
+ retArray = new String[] {"198,", "19998,", "1998,", "19998,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // add
+ sql = "select if(s1=0, 99, if(s1=22.0, 999, 9999)) + if(s1=11, 99, 9999)
from table1";
+ retArray =
+ new String[] {
+ "10098,", "10098,", "10998,", "19998,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // built-in scalar function
+ sql = "select diff(if(s1=0, 99, if(s1>22, 999, 9999))) from table1";
+ retArray = new String[] {"null,", "9900.0,", "0.0,", "-9000.0,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // use in aggregation operation
+ // avg
+ sql = "select avg(if(s1=0, 100, s1)) from table1";
+ retArray = new String[] {"41.5,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // max
+ sql = "select max(if(s1=0, 100, s1)) from table1";
+ retArray = new String[] {"100,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // avg × max
+ sql = "select avg(if(s1=0, 100, s1)) * max(if(s1=0, 100, s1)) from table1";
+ retArray = new String[] {"4150.0,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // UDF
+ }
+
+ @Test
+ public void testKind1UseOtherOperation() {
+ // use scalar function
+ String sql = "select if(sin(s1)>=0, '>=0', '<0') from table1";
+ String[] retArray =
+ new String[] {
+ ">=0,", "<0,", "<0,", ">=0,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // true_value and false_value use scalar function
+ sql = "select if(sin(s1)>=0, abs(s1), abs(s1+1)) from table1";
+ retArray =
+ new String[] {
+ "0,", "12,", "23,", "33,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // use aggregation function
+ sql = "select if(max(s1)>=44, max(s1), min(s1)) from table1";
+ retArray = new String[] {"0,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // UDF
+ }
+
+ @Test
+ public void testKind1UseInWhereClause() {
+ String sql = "select s4 from table2 where if(s3=0, s4>44, if(s3=22, s4>0,
if(time>300, true)))";
+ String[] retArray = new String[] {"66.0,", "77.0,"};
+ tableResultSetEqualTest(sql, new String[] {"s4"}, retArray, DATABASE);
+
+ sql = "select if(s3=0, s4>44, if(s3=22, s4>0, if(time>300, true))) from
table2";
+ retArray =
+ new String[] {
+ "false,", "null,", "true,", "true,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // CASE time
+ sql = "select s4 from table2 where if(time=0, false, if(time=300, true,
if(time=200, true)))";
+ retArray = new String[] {"55.0,", "66.0,"};
+ tableResultSetEqualTest(sql, new String[] {"s4"}, retArray, DATABASE);
+ }
+
+ @Test
+ public void testKind1CaseInIf() {
+ String sql =
+ "select if(s1=0 OR s1=22, cast(case when s1=0 then 99 when s1>22 then
999 end as STRING), 'xxx') from table1";
+ String[] retArray =
+ new String[] {
+ "99,", "xxx,", "null,", "xxx,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+ }
+
+ @Test
+ public void testKind1IfInIf() {
+ String sql =
+ "select if(s1=0 OR s1=22, cast(if(s1=0, 99, if(s1>22, 999)) as
STRING), 'xxx') from table1";
+ String[] retArray =
+ new String[] {
+ "99,", "xxx,", "null,", "xxx,",
+ };
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+ }
+
+ @Test
+ public void testKind1Logic() {
+ String sql =
+ "select if(s3 >= 0 and s3 < 20 and s4 >= 50 and s4 < 60, 'value1',
if(s3 >= 20 and s3 < 40 and s4 >= 70 and s4 < 80, 'value2')) from table2";
+ String[] retArray = new String[] {"null,", "value1,", "null,", "value2,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+ }
+
+ @Test
+ public void testReturnValueWithBinarySameConstant() {
+ String sql = "select if(true, 200 + (s1 - 200)) as result FROM table1";
+ String[] expectedHeader = new String[] {"result"};
+ String[] retArray = new String[] {"0,", "11,", "22,", "33,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+ }
+
+ @Test
+ public void testIfWithText() {
+ String sql = "select if(s6 like 'text%', 'true', 'false') FROM table1";
+ String[] retArray = new String[] {"true,", "true,", "true,", "true,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ sql = "select if(s6 is not null, 'true', 'false') FROM table1";
+ retArray = new String[] {"true,", "true,", "true,", "true,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ sql = "select if(s6 in ('text100', 'text200'), 'true', 'false') FROM
table1";
+ retArray = new String[] {"true,", "true,", "false,", "false,"};
+ tableResultSetEqualTest(sql, expectedHeader, retArray, DATABASE);
+
+ // REGEXP does not work yet
+ }
+}