diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBCaseWhenThenIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBCaseWhenThenIT.java index a88b89e90b5da..8a7933ffecffd 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBCaseWhenThenIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/query/IoTDBCaseWhenThenIT.java @@ -928,4 +928,30 @@ public void testThenWithBinarySameConstant() { new String[] {"0,0.0,", "1000000,11.0,", "20000000,22.0,", "210000000,33.0,"}; resultSetEqualTest(sql, expectedHeader, retArray); } + + @Test + public void testNullableAndInLaterCaseBranch() { + prepareData( + new String[] { + "CREATE TIMESERIES root.sg.case_null.s1 WITH DATATYPE=INT32, ENCODING=PLAIN", + "CREATE TIMESERIES root.sg.case_null.s2 WITH DATATYPE=INT32, ENCODING=PLAIN", + "INSERT INTO root.sg.case_null(time, s2) VALUES (1, 1)", + "INSERT INTO root.sg.case_null(time, s1, s2) VALUES (2, 1, 1), (3, 0, 1), (4, 101, 1)" + }); + resultSetEqualTest( + "SELECT CASE WHEN s1 > 100 THEN 1 WHEN s1 > 0 AND s2 > 0 THEN 2 ELSE 3 END AS result " + + "FROM root.sg.case_null", + new String[] {TIMESTAMP_STR, "result"}, + new String[] {"1,3.0,", "2,2.0,", "3,3.0,", "4,1.0,"}); + resultSetEqualTest( + "SELECT CASE WHEN s1 > 100 THEN 1 WHEN NOT (s1 > 0 AND s2 > 0) THEN 2 ELSE 3 END AS result " + + "FROM root.sg.case_null", + new String[] {TIMESTAMP_STR, "result"}, + new String[] {"1,3.0,", "2,3.0,", "3,2.0,", "4,1.0,"}); + resultSetEqualTest( + "SELECT CASE WHEN s1 > 100 THEN false WHEN s2 > 0 THEN (s1 > 0 AND s2 > 0) END AS result " + + "FROM root.sg.case_null", + new String[] {TIMESTAMP_STR, "result"}, + new String[] {"1,null,", "2,true,", "3,false,", "4,false,"}); + } } diff --git a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/LogicAndColumnTransformer.java b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/LogicAndColumnTransformer.java index 5e7197eaab893..69cf49b96445b 100644 --- a/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/LogicAndColumnTransformer.java +++ b/iotdb-core/calc-commons/src/main/java/org/apache/iotdb/calc/transformation/dag/column/binary/LogicAndColumnTransformer.java @@ -77,7 +77,7 @@ public void evaluateWithSelection(boolean[] selection) { } } - initializeColumnCache(rightColumn); + initializeColumnCache(builder.build()); this.leftTransformer.clearCache(); this.rightTransformer.clearCache(); } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/LogicBinaryColumnTransformerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/LogicBinaryColumnTransformerTest.java index 086a143c86d33..018dcefce5fdf 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/LogicBinaryColumnTransformerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/transformation/dag/column/binary/LogicBinaryColumnTransformerTest.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.queryengine.transformation.dag.column.binary; +import org.apache.iotdb.calc.transformation.dag.column.ColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.binary.BinaryColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.binary.LogicAndColumnTransformer; import org.apache.iotdb.calc.transformation.dag.column.binary.LogicOrColumnTransformer; @@ -37,6 +38,7 @@ import org.junit.Test; import java.util.Arrays; +import java.util.function.BiFunction; public class LogicBinaryColumnTransformerTest { @@ -44,6 +46,14 @@ public class LogicBinaryColumnTransformerTest { private static final int POSITION_COUNT = 4; + private static final Boolean[] AND_EXPECTED = { + null, false, null, false, false, false, null, false, true + }; + + private static final Boolean[] OR_EXPECTED = { + null, null, true, null, false, true, true, true, true + }; + private static final boolean[] leftInput = new boolean[] {true, false, true, false}; private static final boolean[] rightInput = new boolean[] {true, false, true, false}; @@ -113,4 +123,84 @@ public void testInputType() { } } + + @Test + public void testLogicAndWithNullsAndSelection() { + assertSelected( + (left, right) -> new LogicAndColumnTransformer(returnType, left, right), + AND_EXPECTED, + false); + } + + @Test + public void testLogicAndWithNullsAndPartialSelection() { + assertSelected( + (left, right) -> new LogicAndColumnTransformer(returnType, left, right), + AND_EXPECTED, + true); + } + + @Test + public void testLogicOrWithNullsAndSelection() { + assertSelected( + (left, right) -> new LogicOrColumnTransformer(returnType, left, right), OR_EXPECTED, false); + } + + @Test + public void testLogicOrWithNullsAndPartialSelection() { + assertSelected( + (left, right) -> new LogicOrColumnTransformer(returnType, left, right), OR_EXPECTED, true); + } + + private void assertSelected( + BiFunction transformerFactory, + Boolean[] expected, + boolean partial) { + Boolean[] values = {null, false, true}; + TsBlockBuilder builder = + new TsBlockBuilder(9, Arrays.asList(TSDataType.BOOLEAN, TSDataType.BOOLEAN)); + for (Boolean left : values) { + for (Boolean right : values) { + builder.getTimeColumnBuilder().writeLong(0); + if (left == null) { + builder.getColumnBuilder(0).appendNull(); + } else { + builder.getColumnBuilder(0).writeBoolean(left); + } + if (right == null) { + builder.getColumnBuilder(1).appendNull(); + } else { + builder.getColumnBuilder(1).writeBoolean(right); + } + builder.declarePosition(); + } + } + TsBlock block = builder.build(); + IdentityColumnTransformer left = new IdentityColumnTransformer(returnType, 0); + IdentityColumnTransformer right = new IdentityColumnTransformer(returnType, 1); + left.addReferenceCount(); + right.addReferenceCount(); + left.initFromTsBlock(block); + right.initFromTsBlock(block); + BinaryColumnTransformer transformer = transformerFactory.apply(left, right); + transformer.addReferenceCount(); + boolean[] selection = new boolean[9]; + for (int i = 0; i < selection.length; i++) { + // Keep the right-FALSE rows (1 and 7) selected while excluding some left-FALSE rows. + selection[i] = !partial || (i != 3 && i != 5); + } + boolean[] selected = selection.clone(); + transformer.evaluateWithSelection(selection); + Assert.assertArrayEquals(selected, selection); + Column result = transformer.getColumn(); + Assert.assertEquals(expected.length, result.getPositionCount()); + for (int i = 0; i < expected.length; i++) { + if (!selected[i] || expected[i] == null) { + Assert.assertTrue(result.isNull(i)); + } else { + Assert.assertFalse(result.isNull(i)); + Assert.assertEquals(expected[i], result.getBoolean(i)); + } + } + } }