Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import java.util.List;
import java.util.Map;

import static org.apache.iotdb.db.it.utils.TestUtils.resultSetEqualTest;
import static org.junit.Assert.fail;

@RunWith(IoTDBTestRunner.class)
Expand Down Expand Up @@ -1127,6 +1128,22 @@ public void selectWithNonExistMeasurementInWhereClause() {
}
}

@Test
public void selectWithNonExistMeasurementInAlwaysTrueWhereClause() {
// root.vehicle.d1 has no s1, so its predicate is always true, while root.vehicle.d0 is still
// filtered by its own s1
resultSetEqualTest(
"select s0 from root.vehicle.* where s1 is null align by device",
new String[] {"Time", "Device", "s0"},
new String[] {
"106,root.vehicle.d0,99,", "1,root.vehicle.d1,999,", "1000,root.vehicle.d1,888,",
});
resultSetEqualTest(
"select count(s0) from root.vehicle.* where s1 is null align by device",
new String[] {"Device", "count(s0)"},
new String[] {"root.vehicle.d0,1,", "root.vehicle.d1,2,"});
}

/**
* data structure D, time, s1, s2; d1: 1, 10, 20; d2: 1, 12, 22; d3: 1, null, 33
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,4 +279,35 @@ public void testSeriesNotExist() {
expectedHeader,
retArray);
}

@Test
public void testSeriesNotExistInWhereAlignByDevice() {
// a predicate on a series that the device does not have is always true for that device
String[] expectedHeader = new String[] {TIMESTAMP_STR, "Device", "s1", "s3", "s4"};
String[] retArray =
new String[] {
"1,root.test.sg1,1,true,false,",
"2,root.test.sg1,2,true,null,",
"3,root.test.sg1,3,null,false,",
};
resultSetEqualTest(
"select s1, s3, s4 from root.** where notExist is null align by device",
expectedHeader,
retArray);
resultSetEqualTest(
"select s1, s3, s4 from root.** where not(notExist > 0) align by device",
expectedHeader,
retArray);
resultSetEqualTest(
"select s1, s3, s4 from root.** where notExist is null and time > 1 align by device",
expectedHeader,
new String[] {"2,root.test.sg1,2,true,null,", "3,root.test.sg1,3,null,false,"});

expectedHeader = new String[] {"Device", "count(s1)"};
retArray = new String[] {"root.test.sg1,3,"};
resultSetEqualTest(
"select count(s1) from root.** where notExist is null align by device",
expectedHeader,
retArray);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1485,9 +1485,12 @@ private void analyzeDeviceToSource(Analysis analysis, QueryStatement queryStatem
deviceToWhereExpression.entrySet()) {
IDeviceID deviceName = deviceWhereExpressionEntry.getKey();
Expression whereExpression = deviceWhereExpressionEntry.getValue();
deviceToSourceExpressions
.computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
.addAll(searchSourceExpressions(whereExpression));
// null means the predicate of this device is always true, so it needs no value filter
if (whereExpression != null) {
deviceToSourceExpressions
.computeIfAbsent(deviceName, key -> new LinkedHashSet<>())
.addAll(searchSourceExpressions(whereExpression));
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimestampOperand;
import org.apache.iotdb.db.queryengine.plan.expression.multi.FunctionExpression;
import org.apache.iotdb.db.queryengine.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.queryengine.plan.parser.StatementGenerator;
import org.apache.iotdb.db.queryengine.plan.statement.Statement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.QueryStatement;
Expand Down Expand Up @@ -387,6 +388,49 @@ public void testRawDataQueryAlignByDevice() {
}
}

@Test
public void testAlignByDeviceWithAlwaysTruePredicate() throws IllegalPathException {
// root.sg.d2 has no s3, so its predicate is simplified to TRUE and needs no value filter
String[] sqls =
new String[] {
"select s1 from root.sg.d2 where s3 is null align by device",
"select s1 from root.sg.d2 where not(s3 > 1) align by device"
};
for (String sql : sqls) {
Analysis analysis = analyzeSQL(sql);

Assert.assertNull(sql, analysis.getDeviceToWhereExpression().get(DEVICE2));
Assert.assertFalse(sql, analysis.hasValueFilter());
assertEquals(
sql,
ImmutableMap.of(
DEVICE2, Sets.newHashSet(new TimeSeriesOperand(new PartialPath("root.sg.d2.s1")))),
analysis.getDeviceToSourceExpressions());
}
}

@Test
public void testAlignByDeviceWithAlwaysTruePredicateOnPartOfDevices()
throws IllegalPathException {
// root.sg.d1 is still filtered by its own s3, while the predicate of root.sg.d2 is TRUE
Analysis analysis = analyzeSQL("select s1 from root.sg.* where s3 is null align by device");

assertEquals(
new IsNullExpression(new TimeSeriesOperand(new PartialPath("root.sg.d1.s3")), false),
analysis.getDeviceToWhereExpression().get(DEVICE1));
Assert.assertNull(analysis.getDeviceToWhereExpression().get(DEVICE2));
Assert.assertTrue(analysis.hasValueFilter());
assertEquals(
ImmutableMap.of(
DEVICE1,
Sets.newHashSet(
new TimeSeriesOperand(new PartialPath("root.sg.d1.s1")),
new TimeSeriesOperand(new PartialPath("root.sg.d1.s3"))),
DEVICE2,
Sets.newHashSet(new TimeSeriesOperand(new PartialPath("root.sg.d2.s1")))),
analysis.getDeviceToSourceExpressions());
}

@Test
public void testAggregationQueryAlignByDevice() {
String sql =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.apache.iotdb.db.queryengine.plan.expression.binary.LogicAndExpression;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.ConstantOperand;
import org.apache.iotdb.db.queryengine.plan.expression.leaf.TimeSeriesOperand;
import org.apache.iotdb.db.queryengine.plan.expression.unary.IsNullExpression;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.DeviceViewNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.FilterNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.GroupByLevelNode;
Expand Down Expand Up @@ -370,6 +371,32 @@ public void testRawDataQueryAlignByDevice() {
Assert.assertEquals(actualPlan, limitNode);
}

@Test
public void testRawDataQueryAlignByDeviceWithAlwaysTruePredicate() {
// root.sg.d2 has no s3, so its predicate is always true and only root.sg.d1 needs a FilterNode
String sql = "SELECT s1 FROM root.sg.* WHERE s3 IS NULL ALIGN BY DEVICE";

PlanNode actualPlan = parseSQLToPlanNode(sql);
Assert.assertTrue(actualPlan instanceof DeviceViewNode);
DeviceViewNode deviceViewNode = (DeviceViewNode) actualPlan;
Assert.assertEquals(
Arrays.asList(
IDeviceID.Factory.DEFAULT_FACTORY.create("root.sg.d1"),
IDeviceID.Factory.DEFAULT_FACTORY.create("root.sg.d2")),
deviceViewNode.getDevices());

PlanNode d1SubPlan = deviceViewNode.getChildren().get(0);
Assert.assertTrue(d1SubPlan instanceof FilterNode);
Assert.assertEquals(
new IsNullExpression(new TimeSeriesOperand(schemaMap.get("root.sg.d1.s3")), false),
((FilterNode) d1SubPlan).getPredicate());

PlanNode d2SubPlan = deviceViewNode.getChildren().get(1);
Assert.assertTrue(d2SubPlan instanceof SeriesScanNode);
Assert.assertEquals(
schemaMap.get("root.sg.d2.s1"), ((SeriesScanNode) d2SubPlan).getSeriesPath());
}

@Test
public void testSimpleAggregationQuery() {
String sql =
Expand Down
Loading