diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java index 2132df0af1d5f..8c5143833e4b0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java @@ -597,6 +597,19 @@ private void calculateSeriesScanOptionsList() { } } + // Using getSeriesScanOptionsBuilder to create SeriesScanBuilder will cause multiple + // calls to setTimeFilterForTableModel and generate a deeply nested Or filter. + // Therefore, a separate setting is made here + Filter timeFilter = null; + if (node.getTimePredicate().isPresent()) { + Expression timePredicate = node.getTimePredicate().get(); + timeFilter = timePredicate.accept(new ConvertPredicateToTimeFilterVisitor(), null); + context + .getDriverContext() + .getFragmentInstanceContext() + .setTimeFilterForTableModel(timeFilter); + } + boolean canPushDownLimit = cannotPushDownConjuncts.isEmpty(); // only use full outer time join boolean canPushDownLimitToAllSeriesScanOptions = @@ -621,10 +634,9 @@ private void calculateSeriesScanOptionsList() { : (pushDownPredicatesForCurrentMeasurement == null ? null : IrUtils.combineConjuncts(pushDownPredicatesForCurrentMeasurement)); - SeriesScanOptions.Builder builder = - node.getTimePredicate() - .map(expression -> getSeriesScanOptionsBuilder(context, expression)) - .orElseGet(SeriesScanOptions.Builder::new); + SeriesScanOptions.Builder builder = new SeriesScanOptions.Builder(); + // time filter may be stateful, so we need to copy it + builder.withGlobalTimeFilter(timeFilter == null ? null : timeFilter.copy()); builder .withIsTableViewForTreeModel(true) .withAllSensors(new HashSet<>(measurementColumnNames)); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonAlignedTreeDeviceViewScanOperatorTreeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonAlignedTreeDeviceViewScanOperatorTreeTest.java index 73f957ddf7880..2188a04479bb7 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonAlignedTreeDeviceViewScanOperatorTreeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/NonAlignedTreeDeviceViewScanOperatorTreeTest.java @@ -67,6 +67,8 @@ import org.apache.tsfile.read.common.type.Type; import org.apache.tsfile.read.common.type.TypeEnum; import org.apache.tsfile.read.common.type.TypeFactory; +import org.apache.tsfile.read.filter.basic.Filter; +import org.apache.tsfile.read.filter.operator.Or; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.write.schema.IMeasurementSchema; import org.junit.After; @@ -86,6 +88,7 @@ import static org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; import static org.apache.iotdb.db.queryengine.execution.operator.Operator.NOT_BLOCKED; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -199,6 +202,37 @@ public void testScanWithPushDownPredicateAndLimitAndOffset() throws Exception { } } + @Test + public void testScanWithPushDownPredicateAndLimitAndOffsetAndTimePredicate() throws Exception { + List outputColumnList = Arrays.asList("sensor0", "sensor1", "sensor2", "time", "tag1"); + TreeNonAlignedDeviceViewScanNode node = getTreeNonAlignedDeviceViewScanNode(outputColumnList); + node.setPushDownOffset(500); + node.setPushDownLimit(500); + node.setTimePredicate( + new ComparisonExpression( + ComparisonExpression.Operator.GREATER_THAN, + new Symbol("time").toSymbolReference(), + new LongLiteral("0"))); + node.setPushDownPredicate( + new ComparisonExpression( + ComparisonExpression.Operator.GREATER_THAN, + new Symbol("sensor1").toSymbolReference(), + new LongLiteral("1000"))); + ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "test-instance-notification"); + Operator operator = getOperator(node, instanceNotificationExecutor); + assertTrue(operator instanceof DeviceIteratorScanOperator); + try { + checkResult(operator, outputColumnList, 500); + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } finally { + operator.close(); + instanceNotificationExecutor.shutdown(); + } + } + @Test public void testScanWithPushDownPredicateAndPushLimitToEachDevice() throws Exception { List outputColumnList = Arrays.asList("sensor0", "sensor1", "sensor2", "time", "tag1"); @@ -633,6 +667,11 @@ private void checkResult(Operator operator, List outputColumnList, int e } count += tsBlock.getPositionCount(); } + FragmentInstanceContext fragmentInstance = operator.getOperatorContext().getInstanceContext(); + Filter globalTimeFilter = fragmentInstance.getGlobalTimeFilter(); + if (globalTimeFilter != null) { + assertFalse(globalTimeFilter instanceof Or); + } assertEquals(expectedCount, count); }