Repository navigation
Fix nullable AND results in tree-model CASE #18875
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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,13 +38,22 @@ | |
| import org.junit.Test; | ||
|
|
||
| import java.util.Arrays; | ||
| import java.util.function.BiFunction; | ||
|
|
||
| public class LogicBinaryColumnTransformerTest { | ||
|
|
||
| private static final Type returnType = TypeFactory.getType(TSDataType.BOOLEAN); | ||
|
|
||
| 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<ColumnTransformer, ColumnTransformer, BinaryColumnTransformer> 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)); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This branch only checks
The loop also reads Suggestion: keep a copy of the selection and assert non-null explicitly. boolean[] selected = selection.clone();
transformer.evaluateWithSelection(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));
}
}With these assertions, the pre-fix code and both changes above fail, and the current code passes. Minor: the partial mask |
||
| } | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the AND only appears as a WHEN condition, NULL and FALSE both mean "no match", so this query only covers the
NULL AND TRUEhalf of the fix (time 1:2.0before the fix,3.0after). Time 3 (0 > 0 AND 1 > 0) is3.0whether the AND returns FALSE or NULL: with line 73 ofLogicAndColumnTransformerchanged tobuilder.appendNull(), this query still returns3.0, 2.0, 3.0, 1.0.A query that exposes the AND result itself would cover the FALSE half too. On the same data:
CASE WHEN s1 > 100 THEN 1 WHEN NOT (s1 > 0 AND s2 > 0) THEN 2 ELSE 3 END3.0, 3.0, 3.0, 1.03.0, 3.0, 2.0, 1.0CASE WHEN s1 > 100 THEN false WHEN s2 > 0 THEN (s1 > 0 AND s2 > 0) ENDtrue, true, null, falsenull, true, false, falseBoth also fail with the
appendNull()change (time 3 becomes3.0/null). I got these values by running the expressions throughColumnTransformerVisitor+FilterAndProjectOperatoron this PR's code, with and without the fix.