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 67fda53e0c1 feat: show timeseries [order by timseries] clause (#17065)
67fda53e0c1 is described below
commit 67fda53e0c11e341639948bec495f96ff1483f3f
Author: xiangmy21 <[email protected]>
AuthorDate: Fri Feb 6 11:41:04 2026 +0800
feat: show timeseries [order by timseries] clause (#17065)
---
.../IoTDBShowTimeseriesOrderByTimeseriesIT.java | 278 +++++++++++++++++++++
.../org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 9 +-
.../impl/DataNodeInternalRPCServiceImpl.java | 16 +-
.../schema/source/LogicalViewSchemaSource.java | 3 +-
.../schema/source/SchemaSourceFactory.java | 16 +-
.../schema/source/TimeSeriesSchemaSource.java | 9 +-
.../db/queryengine/plan/parser/ASTVisitor.java | 10 +
.../plan/planner/LogicalPlanBuilder.java | 8 +-
.../plan/planner/LogicalPlanVisitor.java | 69 +++--
.../plan/planner/OperatorTreeGenerator.java | 3 +-
.../SimpleFragmentParallelPlanner.java | 4 +-
.../metadata/read/TimeSeriesSchemaScanNode.java | 49 +++-
.../node/process/ActiveRegionScanMergeNode.java | 3 +
.../plan/statement/metadata/ShowStatement.java | 7 +
.../metadata/ShowTimeSeriesStatement.java | 15 ++
.../apache/iotdb/db/schemaengine/SchemaEngine.java | 21 ++
.../schemaregion/impl/SchemaRegionMemoryImpl.java | 8 +
.../mtree/impl/mem/MTreeBelowSGMemoryImpl.java | 198 +++++++++++++--
.../mtree/impl/mem/mnode/IMemMNode.java | 11 +-
.../mtree/impl/mem/mnode/basic/BasicMNode.java | 16 +-
.../impl/mem/mnode/impl/AboveDatabaseMNode.java | 10 +
.../mtree/impl/mem/mnode/impl/DatabaseMNode.java | 10 +
.../impl/mem/mnode/impl/MeasurementMNode.java | 10 +
.../mtree/impl/pbtree/MTreeBelowSGCachedImpl.java | 148 +++++++++++
.../schemaregion/read/req/IShowTimeSeriesPlan.java | 4 +
.../read/req/SchemaRegionReadPlanFactory.java | 14 +-
.../read/req/impl/ShowTimeSeriesPlanImpl.java | 18 +-
.../impl/SchemaReaderLimitOffsetWrapper.java | 36 ++-
.../template/ClusterTemplateManager.java | 8 +-
.../schemaRegion/SchemaRegionTestUtil.java | 25 +-
.../schema/SchemaQueryScanOperatorTest.java | 3 +-
31 files changed, 951 insertions(+), 88 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBShowTimeseriesOrderByTimeseriesIT.java
b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBShowTimeseriesOrderByTimeseriesIT.java
new file mode 100644
index 00000000000..8218d9ef511
--- /dev/null
+++
b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/IoTDBShowTimeseriesOrderByTimeseriesIT.java
@@ -0,0 +1,278 @@
+/*
+ * 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.db.it.schema;
+
+import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.util.AbstractSchemaIT;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runners.Parameterized;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBShowTimeseriesOrderByTimeseriesIT extends AbstractSchemaIT {
+
+ private static final List<String> BASE_TIMESERIES_DB1 =
+ Arrays.asList("root.db1.devA.m1", "root.db1.devB.m1",
"root.db1.devA.m2", "root.db1.devB.x");
+ private static final List<String> BASE_TIMESERIES_DB2 =
+ Arrays.asList("root.db2.devA.m1", "root.db2.devC.m3",
"root.db2.devC.m0");
+ private static final List<String> BASE_TIMESERIES = // combine db1 and db2
+ Stream.concat(BASE_TIMESERIES_DB1.stream(), BASE_TIMESERIES_DB2.stream())
+ .collect(Collectors.toList());
+
+ public IoTDBShowTimeseriesOrderByTimeseriesIT(SchemaTestMode schemaTestMode)
{
+ super(schemaTestMode);
+ }
+
+ @Parameterized.BeforeParam
+ public static void before() throws Exception {
+ setUpEnvironment();
+ EnvFactory.getEnv().initClusterEnvironment();
+ }
+
+ @Parameterized.AfterParam
+ public static void after() throws Exception {
+ EnvFactory.getEnv().cleanClusterEnvironment();
+ tearDownEnvironment();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ clearSchema();
+ }
+
+ private void prepareComplexSchema() throws Exception {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE root.db1");
+ statement.execute("CREATE DATABASE root.db2");
+
+ for (String ts : BASE_TIMESERIES) {
+ statement.execute(
+ String.format(
+ "create timeseries %s with datatype=INT32, encoding=RLE,
compression=SNAPPY", ts));
+ }
+ }
+ }
+
+ private List<String> queryTimeseries(final String sql) throws Exception {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement();
+ ResultSet resultSet = statement.executeQuery(sql)) {
+ List<String> result = new ArrayList<>();
+ while (resultSet.next()) {
+ result.add(resultSet.getString(ColumnHeaderConstant.TIMESERIES));
+ }
+ return result;
+ }
+ }
+
+ @Test
+ public void testOrderAscWithoutLimit() throws Exception {
+ prepareComplexSchema();
+ List<String> expected = new ArrayList<>(BASE_TIMESERIES);
+ Collections.sort(expected);
+
+ List<String> actual = queryTimeseries("show timeseries root.db*.** order
by timeseries");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testOrderDescWithOffsetLimit() throws Exception {
+ prepareComplexSchema();
+ List<String> expected = new ArrayList<>(BASE_TIMESERIES_DB1);
+ Collections.sort(expected);
+ Collections.reverse(expected);
+ expected = expected.subList(1, 3); // offset 1 limit 2
+
+ List<String> actual =
+ queryTimeseries("show timeseries root.db1.** order by timeseries desc
offset 1 limit 2");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testInsertThenQueryOrder() throws Exception {
+ prepareComplexSchema();
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute(
+ "create timeseries root.db1.devX.a with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ }
+
+ List<String> expected = new ArrayList<>(BASE_TIMESERIES_DB1);
+ expected.add("root.db1.devX.a");
+ Collections.sort(expected);
+
+ List<String> actual = queryTimeseries("show timeseries root.db1.** order
by timeseries");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testDeleteSubtreeThenQueryOrder() throws Exception {
+ prepareComplexSchema();
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("delete timeseries root.db2.devC.**");
+ }
+
+ List<String> expected = new ArrayList<>(BASE_TIMESERIES_DB2);
+ expected.remove("root.db2.devC.m0");
+ expected.remove("root.db2.devC.m3");
+ Collections.sort(expected);
+
+ List<String> actual = queryTimeseries("show timeseries root.db2.** order
by timeseries");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testOffsetLimitAfterDeletesAndAdds() throws Exception {
+ prepareComplexSchema();
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("delete timeseries root.db1.devB.x");
+ statement.execute(
+ "create timeseries root.db1.devC.m0 with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ statement.execute(
+ "create timeseries root.db1.devZ.z with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ }
+
+ List<String> expected = new ArrayList<>(BASE_TIMESERIES_DB1);
+ expected.remove("root.db1.devB.x");
+ expected.add("root.db1.devC.m0");
+ expected.add("root.db1.devZ.z");
+ Collections.sort(expected);
+ expected = expected.subList(2, 4); // offset 2 limit 2
+
+ List<String> actual =
+ queryTimeseries("show timeseries root.db1.** order by timeseries
offset 2 limit 2");
+ assertEquals(expected, actual);
+ }
+
+ @Test
+ public void testConflictWithLatest() throws Exception {
+ prepareComplexSchema();
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ try (ResultSet ignored =
+ statement.executeQuery("show latest timeseries order by
timeseries")) {
+ fail("Expected exception for conflict between LATEST and ORDER BY
TIMESERIES");
+ } catch (SQLException e) {
+ assertTrue(
+ e.getMessage().toLowerCase().contains("latest")
+ && e.getMessage().toLowerCase().contains("order by
timeseries"));
+ }
+ }
+ }
+
+ @Test
+ public void testOrderByWithTimeCondition() throws Exception {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE root.db1");
+ statement.execute(
+ "create timeseries root.db1.devA.s1 with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ statement.execute(
+ "create timeseries root.db1.devA.s2 with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ statement.execute(
+ "create timeseries root.db1.devB.s1 with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+
+ // s1 has points in [1..5], s2 only [1..3], devB.s1 only [4..5]
+ for (int t = 1; t <= 5; t++) {
+ statement.execute(
+ String.format("insert into root.db1.devA(timestamp, s1) values
(%d, %d)", t, t));
+ }
+ for (int t = 1; t <= 3; t++) {
+ statement.execute(
+ String.format("insert into root.db1.devA(timestamp, s2) values
(%d, %d)", t, t));
+ }
+ for (int t = 4; t <= 5; t++) {
+ statement.execute(
+ String.format("insert into root.db1.devB(timestamp, s1) values
(%d, %d)", t, t));
+ }
+ }
+
+ List<String> actual =
+ queryTimeseries("show timeseries root.db1.** where time > 3 order by
timeseries desc");
+ assertEquals(Arrays.asList("root.db1.devB.s1", "root.db1.devA.s1"),
actual);
+ }
+
+ @Test
+ public void testWhereClauseOffsetAppliedAfterFilter() throws Exception {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE root.ln");
+ statement.execute(
+ "create timeseries root.ln.wf01.wt01.status with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ statement.execute(
+ "create timeseries root.ln.wf02.wt01.status with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ statement.execute(
+ "create timeseries root.ln.wf02.wt02.status with datatype=INT32,
encoding=RLE, compression=SNAPPY");
+ }
+
+ List<String> actual =
+ queryTimeseries(
+ "show timeseries root.ln.** where timeseries contains 'wf02.wt'
order by timeseries offset 1 limit 1");
+ assertEquals(Collections.singletonList("root.ln.wf02.wt02.status"),
actual);
+ }
+
+ @Test
+ public void testAlterTemplateUpdatesOffsetOrder() throws Exception {
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("CREATE DATABASE root.sg1");
+ statement.execute("create device template t1 (s1 INT32, s0 INT32)");
+ statement.execute("set device template t1 to root.sg1.d1");
+ statement.execute("create timeseries using device template on
root.sg1.d1");
+ statement.execute("set device template t1 to root.sg1.d2");
+ statement.execute("create timeseries using device template on
root.sg1.d2");
+ }
+
+ List<String> before =
+ queryTimeseries("show timeseries root.sg1.** order by timeseries desc
offset 2 limit 2");
+ assertEquals(Arrays.asList("root.sg1.d1.s1", "root.sg1.d1.s0"), before);
+
+ try (Connection connection = EnvFactory.getEnv().getConnection();
+ Statement statement = connection.createStatement()) {
+ statement.execute("alter device template t1 add (s00 INT32)");
+ }
+
+ List<String> after =
+ queryTimeseries("show timeseries root.sg1.** order by timeseries
offset 3 limit 3");
+ assertEquals(Arrays.asList("root.sg1.d2.s0", "root.sg1.d2.s00",
"root.sg1.d2.s1"), after);
+ }
+}
diff --git
a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
index efe661e0543..2308d3b81b7 100644
---
a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
+++
b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4
@@ -200,7 +200,12 @@ showDevices
// ---- Show Timeseries
showTimeseries
- : SHOW LATEST? TIMESERIES prefixPath? timeseriesWhereClause?
timeConditionClause? rowPaginationClause?
+ : SHOW LATEST? TIMESERIES prefixPath? timeseriesWhereClause?
timeConditionClause? orderByTimeseriesClause? rowPaginationClause?
+ ;
+
+// order by timeseries for SHOW TIMESERIES
+orderByTimeseriesClause
+ : ORDER BY TIMESERIES (ASC | DESC)?
;
// ---- Show Child Paths
@@ -1586,4 +1591,4 @@ subStringExpression
signedIntegerLiteral
: (PLUS|MINUS)?INTEGER_LITERAL
- ;
\ No newline at end of file
+ ;
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
index 46d377be0a1..00403b8f9ac 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -77,6 +77,7 @@ import
org.apache.iotdb.commons.schema.filter.SchemaFilterFactory;
import org.apache.iotdb.commons.schema.table.TsTable;
import org.apache.iotdb.commons.schema.table.TsTableInternalRPCType;
import org.apache.iotdb.commons.schema.table.TsTableInternalRPCUtil;
+import org.apache.iotdb.commons.schema.template.Template;
import org.apache.iotdb.commons.schema.view.ViewType;
import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpression;
import org.apache.iotdb.commons.service.metric.MetricService;
@@ -186,6 +187,7 @@ import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaRea
import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
import org.apache.iotdb.db.schemaengine.template.ClusterTemplateManager;
import org.apache.iotdb.db.schemaengine.template.TemplateInternalRPCUpdateType;
+import org.apache.iotdb.db.schemaengine.template.TemplateInternalRPCUtil;
import org.apache.iotdb.db.service.DataNode;
import org.apache.iotdb.db.service.RegionMigrateService;
import
org.apache.iotdb.db.service.externalservice.ExternalServiceManagementService;
@@ -2571,7 +2573,19 @@ public class DataNodeInternalRPCServiceImpl implements
IDataNodeRPCService.Iface
ClusterTemplateManager.getInstance().commitTemplatePreSetInfo(req.getTemplateInfo());
break;
case UPDATE_TEMPLATE_INFO:
-
ClusterTemplateManager.getInstance().updateTemplateInfo(req.getTemplateInfo());
+ Template newTemplate =
+ TemplateInternalRPCUtil.parseUpdateTemplateInfoBytes(
+ ByteBuffer.wrap(req.getTemplateInfo()));
+ Template oldTemplate =
+
ClusterTemplateManager.getInstance().getTemplate(newTemplate.getId());
+ ClusterTemplateManager.getInstance().updateTemplateInfo(newTemplate);
+ long delta =
+ newTemplate.getMeasurementNumber()
+ - (oldTemplate == null ? 0 :
oldTemplate.getMeasurementNumber());
+ if (delta != 0) {
+ SchemaEngine.getInstance()
+ .updateSubtreeMeasurementCountForTemplate(newTemplate.getId(),
delta);
+ }
break;
default:
LOGGER.warn("Unsupported type {} when updating template", req.type);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/LogicalViewSchemaSource.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/LogicalViewSchemaSource.java
index f1eaaebbd1d..144ec16d4a3 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/LogicalViewSchemaSource.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/LogicalViewSchemaSource.java
@@ -79,7 +79,8 @@ public class LogicalViewSchemaSource implements
ISchemaSource<ITimeSeriesSchemaI
SchemaFilterFactory.and(
schemaFilter,
SchemaFilterFactory.createViewTypeFilter(ViewType.VIEW)),
true,
- scope));
+ scope,
+ null));
} catch (MetadataException e) {
throw new SchemaExecutionException(e.getMessage(), e);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/SchemaSourceFactory.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/SchemaSourceFactory.java
index 2ef0ab9e18a..87560ad47be 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/SchemaSourceFactory.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/SchemaSourceFactory.java
@@ -26,6 +26,7 @@ import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.commons.schema.table.TsTable;
import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
import org.apache.iotdb.commons.schema.template.Template;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.IDeviceSchemaInfo;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.INodeSchemaInfo;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ITimeSeriesSchemaInfo;
@@ -47,7 +48,7 @@ public class SchemaSourceFactory {
Map<Integer, Template> templateMap,
PathPatternTree scope) {
return new TimeSeriesSchemaSource(
- pathPattern, isPrefixMatch, 0, 0, schemaFilter, templateMap, false,
scope);
+ pathPattern, isPrefixMatch, 0, 0, schemaFilter, templateMap, false,
scope, null);
}
// show time series
@@ -58,9 +59,18 @@ public class SchemaSourceFactory {
long offset,
SchemaFilter schemaFilter,
Map<Integer, Template> templateMap,
- PathPatternTree scope) {
+ PathPatternTree scope,
+ Ordering timeseriesOrdering) {
return new TimeSeriesSchemaSource(
- pathPattern, isPrefixMatch, limit, offset, schemaFilter, templateMap,
true, scope);
+ pathPattern,
+ isPrefixMatch,
+ limit,
+ offset,
+ schemaFilter,
+ templateMap,
+ true,
+ scope,
+ timeseriesOrdering);
}
// count device
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSource.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSource.java
index 40799c548ee..a56cfa228bf 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSource.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSource.java
@@ -29,6 +29,7 @@ import
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.commons.schema.template.Template;
import org.apache.iotdb.commons.schema.view.ViewType;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.req.SchemaRegionReadPlanFactory;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ITimeSeriesSchemaInfo;
@@ -54,6 +55,7 @@ public class TimeSeriesSchemaSource implements
ISchemaSource<ITimeSeriesSchemaIn
private final SchemaFilter schemaFilter;
private final Map<Integer, Template> templateMap;
private final boolean needViewDetail;
+ private final Ordering timeseriesOrdering;
TimeSeriesSchemaSource(
PartialPath pathPattern,
@@ -63,7 +65,8 @@ public class TimeSeriesSchemaSource implements
ISchemaSource<ITimeSeriesSchemaIn
SchemaFilter schemaFilter,
Map<Integer, Template> templateMap,
boolean needViewDetail,
- PathPatternTree scope) {
+ PathPatternTree scope,
+ Ordering timeseriesOrdering) {
this.pathPattern = pathPattern;
this.isPrefixMatch = isPrefixMatch;
this.limit = limit;
@@ -72,6 +75,7 @@ public class TimeSeriesSchemaSource implements
ISchemaSource<ITimeSeriesSchemaIn
this.templateMap = templateMap;
this.needViewDetail = needViewDetail;
this.scope = scope;
+ this.timeseriesOrdering = timeseriesOrdering;
}
@Override
@@ -86,7 +90,8 @@ public class TimeSeriesSchemaSource implements
ISchemaSource<ITimeSeriesSchemaIn
isPrefixMatch,
schemaFilter,
needViewDetail,
- scope));
+ scope,
+ timeseriesOrdering));
} catch (MetadataException e) {
throw new SchemaExecutionException(e.getMessage(), e);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
index 70325257b08..e71a1adaaff 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java
@@ -789,6 +789,16 @@ public class ASTVisitor extends
IoTDBSqlParserBaseVisitor<Statement> {
showTimeSeriesStatement.setTimeCondition(
parseWhereClause(ctx.timeConditionClause().whereClause()));
}
+
+ // ORDER BY TIMESERIES [ASC|DESC]
+ if (ctx.orderByTimeseriesClause() != null) {
+ if (orderByHeat) {
+ throw new SemanticException(
+ "LATEST and ORDER BY TIMESERIES cannot be used at the same time.");
+ }
+ showTimeSeriesStatement.setTimeseriesOrdering(
+ ctx.orderByTimeseriesClause().DESC() != null ? Ordering.DESC :
Ordering.ASC);
+ }
if (ctx.rowPaginationClause() != null) {
if (ctx.rowPaginationClause().limitClause() != null) {
showTimeSeriesStatement.setLimit(parseLimitClause(ctx.rowPaginationClause().limitClause()));
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
index cc194c36aa7..98f856aff83 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanBuilder.java
@@ -937,7 +937,7 @@ public class LogicalPlanBuilder {
}
public LogicalPlanBuilder planLimit(long rowLimit) {
- if (rowLimit == 0) {
+ if (rowLimit <= 0) {
return this;
}
@@ -1027,7 +1027,8 @@ public class LogicalPlanBuilder {
boolean orderByHeat,
boolean prefixPath,
Map<Integer, Template> templateMap,
- PathPatternTree scope) {
+ PathPatternTree scope,
+ Ordering timeseriesOrdering) {
this.root =
new TimeSeriesSchemaScanNode(
context.getQueryId().genPlanNodeId(),
@@ -1038,7 +1039,8 @@ public class LogicalPlanBuilder {
orderByHeat,
prefixPath,
templateMap,
- scope);
+ scope,
+ timeseriesOrdering);
return this;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
index 8a73227dfad..c3ac21641d1 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/LogicalPlanVisitor.java
@@ -18,6 +18,7 @@
*/
package org.apache.iotdb.db.queryengine.plan.planner;
+import org.apache.iotdb.commons.conf.CommonDescriptor;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
import org.apache.iotdb.commons.schema.template.Template;
@@ -54,6 +55,8 @@ import
org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTablet
import
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.AggregationStep;
import org.apache.iotdb.db.queryengine.plan.statement.StatementNode;
import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
+import org.apache.iotdb.db.queryengine.plan.statement.component.SortItem;
import org.apache.iotdb.db.queryengine.plan.statement.crud.DeleteDataStatement;
import
org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement;
@@ -88,6 +91,7 @@ import
org.apache.iotdb.db.queryengine.plan.statement.metadata.view.ShowLogicalV
import
org.apache.iotdb.db.queryengine.plan.statement.pipe.PipeEnrichedStatement;
import
org.apache.iotdb.db.queryengine.plan.statement.sys.ExplainAnalyzeStatement;
import org.apache.iotdb.db.queryengine.plan.statement.sys.ShowQueriesStatement;
+import org.apache.iotdb.db.schemaengine.SchemaEngineMode;
import org.apache.tsfile.enums.TSDataType;
import org.apache.tsfile.file.metadata.IDeviceID;
@@ -95,6 +99,7 @@ import org.apache.tsfile.utils.Pair;
import org.apache.tsfile.write.schema.MeasurementSchema;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
@@ -557,34 +562,52 @@ public class LogicalPlanVisitor extends
StatementVisitor<PlanNode, MPPQueryConte
ShowTimeSeriesStatement showTimeSeriesStatement, MPPQueryContext
context) {
LogicalPlanBuilder planBuilder = new LogicalPlanBuilder(analysis, context);
+ // Ensure TypeProvider has schema query column types for later SortNode
+ ColumnHeaderConstant.showTimeSeriesColumnHeaders.forEach(
+ columnHeader ->
+ context
+ .getTypeProvider()
+ .setTreeModelType(columnHeader.getColumnName(),
columnHeader.getColumnType()));
+
long limit = showTimeSeriesStatement.getLimit();
long offset = showTimeSeriesStatement.getOffset();
+ Ordering timeseriesOrdering =
showTimeSeriesStatement.getTimeseriesOrdering();
+ boolean orderByTimeseries = timeseriesOrdering != null;
+ boolean orderByTimeseriesDesc = timeseriesOrdering == Ordering.DESC;
if (showTimeSeriesStatement.hasTimeCondition()) {
planBuilder =
- planBuilder
-
.planTimeseriesRegionScan(analysis.getDeviceToTimeseriesSchemas(), false)
- .planLimit(limit)
- .planOffset(offset);
+
planBuilder.planTimeseriesRegionScan(analysis.getDeviceToTimeseriesSchemas(),
false);
+ if (orderByTimeseries) {
+ SortItem sortItem = new SortItem(ColumnHeaderConstant.TIMESERIES,
timeseriesOrdering);
+ planBuilder =
planBuilder.planOrderBy(Collections.singletonList(sortItem));
+ }
+ planBuilder = planBuilder.planOffset(offset).planLimit(limit);
return planBuilder.getRoot();
}
// If there is only one region, we can push down the offset and limit
operation to
// source operator.
- boolean canPushDownOffsetLimit =
+ boolean singleSchemaRegion =
analysis.getSchemaPartitionInfo() != null
- && analysis.getSchemaPartitionInfo().getDistributionInfo().size()
== 1
- && !showTimeSeriesStatement.isOrderByHeat();
+ && analysis.getSchemaPartitionInfo().getDistributionInfo().size()
== 1;
+ boolean isMemorySchemaEngine =
+ CommonDescriptor.getInstance()
+ .getConfig()
+ .getSchemaEngineMode()
+ .equals(SchemaEngineMode.Memory.toString());
+ boolean canPushDownOffsetLimit = false;
- if (showTimeSeriesStatement.isOrderByHeat()) {
+ if (showTimeSeriesStatement.isOrderByHeat()
+ || (!isMemorySchemaEngine && orderByTimeseriesDesc)) {
limit = 0;
offset = 0;
- } else if (!canPushDownOffsetLimit) {
- limit =
- showTimeSeriesStatement.getLimit() != 0
- ? showTimeSeriesStatement.getLimit() +
showTimeSeriesStatement.getOffset()
- : 0;
+ } else if (!singleSchemaRegion) {
+ limit = showTimeSeriesStatement.getLimitWithOffset();
offset = 0;
+ } else {
+ canPushDownOffsetLimit = true;
}
+
planBuilder =
planBuilder
.planTimeSeriesSchemaSource(
@@ -595,9 +618,17 @@ public class LogicalPlanVisitor extends
StatementVisitor<PlanNode, MPPQueryConte
showTimeSeriesStatement.isOrderByHeat(),
showTimeSeriesStatement.isPrefixPath(),
analysis.getRelatedTemplateInfo(),
- showTimeSeriesStatement.getAuthorityScope())
+ showTimeSeriesStatement.getAuthorityScope(),
+ timeseriesOrdering)
.planSchemaQueryMerge(showTimeSeriesStatement.isOrderByHeat());
+ // order by timeseries name in multi-region or PBTree-Desc case: still
need global SortNode
+ if (orderByTimeseries
+ && (!singleSchemaRegion || (!isMemorySchemaEngine &&
orderByTimeseriesDesc))) {
+ SortItem sortItem = new SortItem(ColumnHeaderConstant.TIMESERIES,
timeseriesOrdering);
+ planBuilder =
planBuilder.planOrderBy(Collections.singletonList(sortItem));
+ }
+
// show latest timeseries
if (showTimeSeriesStatement.isOrderByHeat()
&& null != analysis.getDataPartitionInfo()
@@ -640,10 +671,7 @@ public class LogicalPlanVisitor extends
StatementVisitor<PlanNode, MPPQueryConte
long limit = showDevicesStatement.getLimit();
long offset = showDevicesStatement.getOffset();
if (!canPushDownOffsetLimit) {
- limit =
- showDevicesStatement.getLimit() != 0
- ? showDevicesStatement.getLimit() +
showDevicesStatement.getOffset()
- : 0;
+ limit = showDevicesStatement.getLimitWithOffset();
offset = 0;
}
@@ -1015,10 +1043,7 @@ public class LogicalPlanVisitor extends
StatementVisitor<PlanNode, MPPQueryConte
long limit = showLogicalViewStatement.getLimit();
long offset = showLogicalViewStatement.getOffset();
if (!canPushDownOffsetLimit) {
- limit =
- showLogicalViewStatement.getLimit() != 0
- ? showLogicalViewStatement.getLimit() +
showLogicalViewStatement.getOffset()
- : 0;
+ limit = showLogicalViewStatement.getLimitWithOffset();
offset = 0;
}
planBuilder =
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
index d0e0ff37ca2..0219de6502d 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/OperatorTreeGenerator.java
@@ -913,7 +913,8 @@ public class OperatorTreeGenerator extends
PlanVisitor<Operator, LocalExecutionP
node.getOffset(),
node.getSchemaFilter(),
node.getTemplateMap(),
- node.getScope()));
+ node.getScope(),
+ node.getTimeseriesOrdering()));
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SimpleFragmentParallelPlanner.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SimpleFragmentParallelPlanner.java
index 334d1973f53..25f51f88d9e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SimpleFragmentParallelPlanner.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SimpleFragmentParallelPlanner.java
@@ -157,7 +157,9 @@ public class SimpleFragmentParallelPlanner extends
AbstractFragmentParallelPlann
|| analysis.getTreeStatement() instanceof ExplainAnalyzeStatement
|| analysis.getTreeStatement() instanceof ShowQueriesStatement
|| (analysis.getTreeStatement() instanceof ShowTimeSeriesStatement
- && ((ShowTimeSeriesStatement)
analysis.getTreeStatement()).isOrderByHeat())) {
+ && (((ShowTimeSeriesStatement)
analysis.getTreeStatement()).isOrderByHeat()
+ || ((ShowTimeSeriesStatement) analysis.getTreeStatement())
+ .isOrderByTimeseries()))) {
fragmentInstance.getFragment().generateTypeProvider(queryContext.getTypeProvider());
}
instanceMap.putIfAbsent(fragment.getId(), fragmentInstance);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesSchemaScanNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesSchemaScanNode.java
index 1997a3caefe..c91f01fd3ce 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesSchemaScanNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesSchemaScanNode.java
@@ -29,6 +29,7 @@ import org.apache.iotdb.commons.schema.template.Template;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeType;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -48,6 +49,9 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
// if is true, the result will be sorted according to the inserting
frequency of the timeseries
private final boolean orderByHeat;
+ // Ordering of timeseries full path in this region, null means no ordering.
+ private final Ordering timeseriesOrdering;
+
private final SchemaFilter schemaFilter;
private final Map<Integer, Template> templateMap;
@@ -66,6 +70,25 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
this.schemaFilter = schemaFilter;
this.orderByHeat = orderByHeat;
this.templateMap = templateMap;
+ this.timeseriesOrdering = null;
+ }
+
+ public TimeSeriesSchemaScanNode(
+ PlanNodeId id,
+ PartialPath partialPath,
+ SchemaFilter schemaFilter,
+ long limit,
+ long offset,
+ boolean orderByHeat,
+ boolean isPrefixPath,
+ @NotNull Map<Integer, Template> templateMap,
+ @NotNull PathPatternTree scope,
+ Ordering timeseriesOrdering) {
+ super(id, partialPath, limit, offset, isPrefixPath, scope);
+ this.schemaFilter = schemaFilter;
+ this.orderByHeat = orderByHeat;
+ this.templateMap = templateMap;
+ this.timeseriesOrdering = timeseriesOrdering;
}
public SchemaFilter getSchemaFilter() {
@@ -82,6 +105,8 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
ReadWriteIOUtils.write(offset, byteBuffer);
ReadWriteIOUtils.write(orderByHeat, byteBuffer);
ReadWriteIOUtils.write(isPrefixPath, byteBuffer);
+ ReadWriteIOUtils.write(timeseriesOrdering != null, byteBuffer);
+ ReadWriteIOUtils.write(timeseriesOrdering == Ordering.DESC, byteBuffer);
ReadWriteIOUtils.write(templateMap.size(), byteBuffer);
for (Template template : templateMap.values()) {
@@ -99,6 +124,8 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
ReadWriteIOUtils.write(offset, stream);
ReadWriteIOUtils.write(orderByHeat, stream);
ReadWriteIOUtils.write(isPrefixPath, stream);
+ ReadWriteIOUtils.write(timeseriesOrdering != null, stream);
+ ReadWriteIOUtils.write(timeseriesOrdering == Ordering.DESC, stream);
ReadWriteIOUtils.write(templateMap.size(), stream);
for (Template template : templateMap.values()) {
@@ -120,6 +147,12 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
long offset = ReadWriteIOUtils.readLong(byteBuffer);
boolean oderByHeat = ReadWriteIOUtils.readBool(byteBuffer);
boolean isPrefixPath = ReadWriteIOUtils.readBool(byteBuffer);
+ boolean orderByTimeseries = ReadWriteIOUtils.readBool(byteBuffer);
+ boolean orderByTimeseriesDesc = ReadWriteIOUtils.readBool(byteBuffer);
+ Ordering timeseriesOrdering = null;
+ if (orderByTimeseries) {
+ timeseriesOrdering = orderByTimeseriesDesc ? Ordering.DESC :
Ordering.ASC;
+ }
int templateNum = ReadWriteIOUtils.readInt(byteBuffer);
Map<Integer, Template> templateMap = new HashMap<>();
@@ -141,13 +174,18 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
oderByHeat,
isPrefixPath,
templateMap,
- scope);
+ scope,
+ timeseriesOrdering);
}
public boolean isOrderByHeat() {
return orderByHeat;
}
+ public Ordering getTimeseriesOrdering() {
+ return timeseriesOrdering;
+ }
+
public Map<Integer, Template> getTemplateMap() {
return templateMap;
}
@@ -168,7 +206,8 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
orderByHeat,
isPrefixPath,
templateMap,
- scope);
+ scope,
+ timeseriesOrdering);
}
@Override
@@ -190,12 +229,14 @@ public class TimeSeriesSchemaScanNode extends
SchemaQueryScanNode {
return false;
}
TimeSeriesSchemaScanNode that = (TimeSeriesSchemaScanNode) o;
- return orderByHeat == that.orderByHeat && Objects.equals(schemaFilter,
that.schemaFilter);
+ return orderByHeat == that.orderByHeat
+ && Objects.equals(timeseriesOrdering, that.timeseriesOrdering)
+ && Objects.equals(schemaFilter, that.schemaFilter);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), schemaFilter, orderByHeat);
+ return Objects.hash(super.hashCode(), schemaFilter, orderByHeat,
timeseriesOrdering);
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/ActiveRegionScanMergeNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/ActiveRegionScanMergeNode.java
index 13718e60437..745b847a106 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/ActiveRegionScanMergeNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/process/ActiveRegionScanMergeNode.java
@@ -70,6 +70,9 @@ public class ActiveRegionScanMergeNode extends
MultiChildProcessNode {
@Override
public List<String> getOutputColumnNames() {
+ if (!children.isEmpty()) {
+ return children.get(0).getOutputColumnNames();
+ }
return outputCount
? ColumnHeaderConstant.countDevicesColumnHeaders.stream()
.map(ColumnHeader::getColumnName)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowStatement.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowStatement.java
index 971fae6698b..b8397918217 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowStatement.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowStatement.java
@@ -59,6 +59,13 @@ public class ShowStatement extends
AuthorityInformationStatement {
this.offset = offset;
}
+ public long getLimitWithOffset() {
+ if (limit <= 0) {
+ return limit;
+ }
+ return limit + offset;
+ }
+
public boolean isPrefixPath() {
return isPrefixPath;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java
index 82a389d6224..40271c9a92b 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java
@@ -22,6 +22,7 @@ package
org.apache.iotdb.db.queryengine.plan.statement.metadata;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import org.apache.iotdb.db.queryengine.plan.statement.component.WhereCondition;
import java.util.Collections;
@@ -42,6 +43,8 @@ public class ShowTimeSeriesStatement extends ShowStatement {
// if is true, the result will be sorted according to the inserting
frequency of the time series
private final boolean orderByHeat;
private WhereCondition timeCondition;
+ // order by timeseries name
+ private Ordering timeseriesOrdering;
public ShowTimeSeriesStatement(PartialPath pathPattern, boolean orderByHeat)
{
super();
@@ -65,6 +68,18 @@ public class ShowTimeSeriesStatement extends ShowStatement {
return orderByHeat;
}
+ public boolean isOrderByTimeseries() {
+ return timeseriesOrdering != null;
+ }
+
+ public Ordering getTimeseriesOrdering() {
+ return timeseriesOrdering;
+ }
+
+ public void setTimeseriesOrdering(Ordering timeseriesOrdering) {
+ this.timeseriesOrdering = timeseriesOrdering;
+ }
+
public void setTimeCondition(WhereCondition timeCondition) {
this.timeCondition = timeCondition;
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
index 842281daa96..dafcaa0d3b0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/SchemaEngine.java
@@ -43,6 +43,7 @@ import
org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion;
import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegionParams;
import org.apache.iotdb.db.schemaengine.schemaregion.SchemaRegionLoader;
import org.apache.iotdb.db.schemaengine.schemaregion.SchemaRegionParams;
+import
org.apache.iotdb.db.schemaengine.schemaregion.impl.SchemaRegionMemoryImpl;
import org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
import org.apache.iotdb.db.schemaengine.template.ClusterTemplateManager;
import org.apache.iotdb.mpp.rpc.thrift.TDataNodeHeartbeatReq;
@@ -253,6 +254,26 @@ public class SchemaEngine {
return new ArrayList<>(schemaRegionMap.keySet());
}
+ public void updateSubtreeMeasurementCountForTemplate(final int templateId,
final long delta) {
+ if (delta == 0) {
+ return;
+ }
+ for (final ISchemaRegion schemaRegion : schemaRegionMap.values()) {
+ if (schemaRegion instanceof SchemaRegionMemoryImpl) {
+ try {
+ ((SchemaRegionMemoryImpl) schemaRegion)
+ .updateSubtreeMeasurementCountForTemplate(templateId, delta);
+ } catch (MetadataException e) {
+ logger.warn(
+ "Failed to update subtree measurement count for template {} in
schemaRegion {}",
+ templateId,
+ schemaRegion.getSchemaRegionId(),
+ e);
+ }
+ }
+ }
+ }
+
public synchronized void createSchemaRegion(
final String storageGroup, final SchemaRegionId schemaRegionId) throws
MetadataException {
if (this.schemaRegionMap == null) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
index 230ed8330ca..0719835fe7a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/impl/SchemaRegionMemoryImpl.java
@@ -1450,6 +1450,14 @@ public class SchemaRegionMemoryImpl implements
ISchemaRegion {
return result;
}
+ public void updateSubtreeMeasurementCountForTemplate(final int templateId,
final long delta)
+ throws MetadataException {
+ if (delta == 0 || mTree == null) {
+ return;
+ }
+ mTree.updateSubtreeMeasurementCountForTemplate(templateId, delta);
+ }
+
@Override
public int fillLastQueryMap(
final PartialPath pattern,
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java
index d409e9e7929..ed519030b63 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/MTreeBelowSGMemoryImpl.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.commons.exception.MetadataException;
import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
+import org.apache.iotdb.commons.path.PathPatternUtil;
import org.apache.iotdb.commons.schema.SchemaConstant;
import org.apache.iotdb.commons.schema.node.role.IDeviceMNode;
import org.apache.iotdb.commons.schema.node.role.IMeasurementMNode;
@@ -48,6 +49,7 @@ import
org.apache.iotdb.db.queryengine.common.schematree.ClusterSchemaTree;
import
org.apache.iotdb.db.queryengine.execution.operator.schema.source.DeviceAttributeUpdater;
import
org.apache.iotdb.db.queryengine.execution.operator.schema.source.DeviceBlackListConstructor;
import
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableId;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import org.apache.iotdb.db.schemaengine.metric.SchemaRegionMemMetric;
import org.apache.iotdb.db.schemaengine.rescon.MemSchemaRegionStatistics;
import
org.apache.iotdb.db.schemaengine.schemaregion.mtree.impl.mem.mnode.IMemMNode;
@@ -194,6 +196,22 @@ public class MTreeBelowSGMemoryImpl {
return store.createSnapshot(snapshotDir);
}
+ private void applySubtreeMeasurementDelta(IMemMNode startNode, final long
delta) {
+ if (delta == 0 || startNode == null) {
+ return;
+ }
+ IMemMNode current = startNode;
+ while (current != null) {
+ current.setSubtreeMeasurementCount(current.getSubtreeMeasurementCount()
+ delta);
+ current = current.getParent();
+ }
+ }
+
+ private long getTemplateMeasurementCount(final int templateId) {
+ final Template template =
ClusterTemplateManager.getInstance().getTemplate(templateId);
+ return template == null ? 0L : template.getMeasurementNumber();
+ }
+
public static MTreeBelowSGMemoryImpl loadFromSnapshot(
final File snapshotDir,
final String databaseFullPath,
@@ -205,18 +223,21 @@ public class MTreeBelowSGMemoryImpl {
final Function<IMeasurementMNode<IMemMNode>, Map<String, String>>
tagGetter,
final Function<IMeasurementMNode<IMemMNode>, Map<String, String>>
attributeGetter)
throws IOException, IllegalPathException {
- return new MTreeBelowSGMemoryImpl(
- PartialPath.getQualifiedDatabasePartialPath(databaseFullPath),
- MemMTreeStore.loadFromSnapshot(
- snapshotDir,
- measurementProcess,
- deviceProcess,
- tableDeviceProcess,
- regionStatistics,
- metric),
- tagGetter,
- attributeGetter,
- regionStatistics);
+ final MTreeBelowSGMemoryImpl mtree =
+ new MTreeBelowSGMemoryImpl(
+ PartialPath.getQualifiedDatabasePartialPath(databaseFullPath),
+ MemMTreeStore.loadFromSnapshot(
+ snapshotDir,
+ measurementProcess,
+ deviceProcess,
+ tableDeviceProcess,
+ regionStatistics,
+ metric),
+ tagGetter,
+ attributeGetter,
+ regionStatistics);
+ mtree.rebuildSubtreeMeasurementCount();
+ return mtree;
}
// endregion
@@ -316,6 +337,7 @@ public class MTreeBelowSGMemoryImpl {
entityMNode.addAlias(alias, measurementMNode);
}
+ applySubtreeMeasurementDelta(measurementMNode.getAsMNode(), 1L);
return measurementMNode;
}
}
@@ -413,6 +435,7 @@ public class MTreeBelowSGMemoryImpl {
if (aliasList != null && aliasList.get(i) != null) {
entityMNode.addAlias(aliasList.get(i), measurementMNode);
}
+ applySubtreeMeasurementDelta(measurementMNode.getAsMNode(), 1L);
measurementMNodeList.add(measurementMNode);
}
return measurementMNodeList;
@@ -621,6 +644,7 @@ public class MTreeBelowSGMemoryImpl {
if (deletedNode.getAlias() != null) {
parent.getAsDeviceMNode().deleteAliasChild(deletedNode.getAlias());
}
+ applySubtreeMeasurementDelta(parent, -1L);
}
deleteEmptyInternalMNode(parent.getAsDeviceMNode());
return deletedNode;
@@ -1027,6 +1051,7 @@ public class MTreeBelowSGMemoryImpl {
entityMNode.setUseTemplate(true);
entityMNode.setSchemaTemplateId(template.getId());
regionStatistics.activateTemplate(template.getId());
+ applySubtreeMeasurementDelta(entityMNode.getAsMNode(), (long)
template.getMeasurementNumber());
}
public Map<PartialPath, List<Integer>> constructSchemaBlackListWithTemplate(
@@ -1091,6 +1116,8 @@ public class MTreeBelowSGMemoryImpl {
resultTemplateSetInfo.put(
node.getPartialPath(),
Collections.singletonList(node.getSchemaTemplateId()));
regionStatistics.deactivateTemplate(node.getSchemaTemplateId());
+ applySubtreeMeasurementDelta(
+ node.getAsMNode(),
-getTemplateMeasurementCount(node.getSchemaTemplateId()));
node.deactivateTemplate();
deleteEmptyInternalMNode(node);
}
@@ -1123,6 +1150,35 @@ public class MTreeBelowSGMemoryImpl {
entityMNode.setUseTemplate(true);
entityMNode.setSchemaTemplateId(templateId);
regionStatistics.activateTemplate(templateId);
+ applySubtreeMeasurementDelta(entityMNode.getAsMNode(),
getTemplateMeasurementCount(templateId));
+ }
+
+ public void updateSubtreeMeasurementCountForTemplate(final int templateId,
final long delta)
+ throws MetadataException {
+ if (delta == 0) {
+ return;
+ }
+ final PartialPath pattern =
+ new PartialPath(
+ databaseMNode.getFullPath()
+ + IoTDBConstant.PATH_SEPARATOR
+ + IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD);
+ try (final EntityUpdater<IMemMNode> updater =
+ new EntityUpdater<IMemMNode>(
+ rootNode, pattern, store, false, SchemaConstant.ALL_MATCH_SCOPE) {
+ @Override
+ protected void updateEntity(final IDeviceMNode<IMemMNode> node) {
+ if (!node.isUseTemplate() || node.getSchemaTemplateId() !=
templateId) {
+ return;
+ }
+ synchronized (MTreeBelowSGMemoryImpl.this) {
+ applySubtreeMeasurementDelta(node.getAsMNode(), delta);
+ }
+ }
+ }) {
+ updater.setSchemaTemplateFilter(templateId);
+ updater.update();
+ }
}
public long countPathsUsingTemplate(final PartialPath pathPattern, final int
templateId)
@@ -1134,6 +1190,23 @@ public class MTreeBelowSGMemoryImpl {
}
}
+ public void rebuildSubtreeMeasurementCount() {
+ rebuildSubtreeMeasurementCountFromNode(rootNode);
+ }
+
+ private long rebuildSubtreeMeasurementCountFromNode(final IMemMNode node) {
+ long count = node.isMeasurement() ? 1L : 0L;
+ final IMNodeIterator<IMemMNode> iterator = store.getChildrenIterator(node);
+ while (iterator.hasNext()) {
+ count += rebuildSubtreeMeasurementCountFromNode(iterator.next());
+ }
+ if (node.isDevice() && node.getAsDeviceMNode().isUseTemplate()) {
+ count +=
getTemplateMeasurementCount(node.getAsDeviceMNode().getSchemaTemplateId());
+ }
+ node.setSubtreeMeasurementCount(count);
+ return count;
+ }
+
// endregion
// region Interfaces for schema reader
@@ -1446,6 +1519,76 @@ public class MTreeBelowSGMemoryImpl {
showTimeSeriesPlan.isPrefixMatch(),
showTimeSeriesPlan.getScope()) {
+ private long remainingOffset =
+ showTimeSeriesPlan.getSchemaFilter() == null ?
showTimeSeriesPlan.getOffset() : 0;
+ private final String[] prunePrefixNodes =
+ getSafePrunePrefixNodes(showTimeSeriesPlan.getPath());
+
+ private String[] getSafePrunePrefixNodes(final PartialPath pattern) {
+ if (pattern == null || !pattern.endWithMultiLevelWildcard()) {
+ return null;
+ }
+ final String[] nodes = pattern.getNodes();
+ return Arrays.copyOf(nodes, nodes.length - 1);
+ }
+
+ private boolean isUnderPrunePrefix(final IMemMNode node) {
+ if (prunePrefixNodes == null) {
+ return false;
+ }
+ final String[] nodePath =
getPartialPathFromRootToNode(node).getNodes();
+ if (nodePath.length < prunePrefixNodes.length) {
+ return false;
+ }
+ for (int i = 0; i < prunePrefixNodes.length; i++) {
+ if (!PathPatternUtil.isNodeMatch(prunePrefixNodes[i],
nodePath[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean shouldPruneSubtree(final IMemMNode node) {
+ if (remainingOffset <= 0) {
+ return false;
+ }
+ if (!isUnderPrunePrefix(node)) {
+ return false;
+ }
+ final long subtreeCount = node.getSubtreeMeasurementCount();
+ if (subtreeCount <= remainingOffset) {
+ remainingOffset -= subtreeCount;
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected boolean acceptFullMatchedNode(final IMemMNode node) {
+ if (!node.isMeasurement()) {
+ return false;
+ }
+ if (remainingOffset > 0) {
+ // skip this measurement
+ remainingOffset--;
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected boolean shouldVisitSubtreeOfInternalMatchedNode(final
IMemMNode node) {
+ if (shouldPruneSubtree(node)) {
+ return false;
+ }
+ return !node.isMeasurement();
+ }
+
+ @Override
+ protected boolean shouldVisitSubtreeOfFullMatchedNode(final
IMemMNode node) {
+ return !node.isMeasurement() && !shouldPruneSubtree(node);
+ }
+
@Override
protected ITimeSeriesSchemaInfo collectMeasurement(
final IMeasurementMNode<IMemMNode> node) {
@@ -1506,15 +1649,41 @@ public class MTreeBelowSGMemoryImpl {
}
};
}
+
+ @Override
+ protected Iterator<IMemMNode> getChildrenIterator(final IMemMNode
parent)
+ throws MetadataException {
+ Iterator<IMemMNode> baseIterator =
super.getChildrenIterator(parent);
+
+ Ordering timeseriesOrdering =
showTimeSeriesPlan.getTimeseriesOrdering();
+ if (timeseriesOrdering == null) {
+ return baseIterator;
+ }
+
+ List<IMemMNode> children = new ArrayList<>();
+ while (baseIterator.hasNext()) {
+ children.add(baseIterator.next());
+ }
+ releaseNodeIterator(baseIterator);
+
+ children.sort(
+ (a, b) -> {
+ int cmp = a.getName().compareTo(b.getName());
+ return timeseriesOrdering == Ordering.DESC ? -cmp : cmp;
+ });
+ return children.iterator();
+ }
};
collector.setTemplateMap(showTimeSeriesPlan.getRelatedTemplate(),
nodeFactory);
final ISchemaReader<ITimeSeriesSchemaInfo> reader =
new TimeseriesReaderWithViewFetch(
collector, showTimeSeriesPlan.getSchemaFilter(),
showTimeSeriesPlan.needViewDetail());
- if (showTimeSeriesPlan.getLimit() > 0 || showTimeSeriesPlan.getOffset() >
0) {
+ final long offsetForWrapper =
+ showTimeSeriesPlan.getSchemaFilter() == null ? 0 :
showTimeSeriesPlan.getOffset();
+ if (showTimeSeriesPlan.getLimit() > 0 || offsetForWrapper > 0) {
return new SchemaReaderLimitOffsetWrapper<>(
- reader, showTimeSeriesPlan.getLimit(),
showTimeSeriesPlan.getOffset());
+ reader, showTimeSeriesPlan.getLimit(), offsetForWrapper);
} else {
return reader;
}
@@ -1626,6 +1795,7 @@ public class MTreeBelowSGMemoryImpl {
measurementMNode.setParent(entityMNode.getAsMNode());
store.addChild(entityMNode.getAsMNode(), leafName,
measurementMNode.getAsMNode());
+ applySubtreeMeasurementDelta(measurementMNode.getAsMNode(), 1L);
return measurementMNode;
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/IMemMNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/IMemMNode.java
index d3d055928b1..6cd14800f3e 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/IMemMNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/IMemMNode.java
@@ -20,4 +20,13 @@ package
org.apache.iotdb.db.schemaengine.schemaregion.mtree.impl.mem.mnode;
import org.apache.iotdb.commons.schema.node.IMNode;
-public interface IMemMNode extends IMNode<IMemMNode> {}
+public interface IMemMNode extends IMNode<IMemMNode> {
+
+ /**
+ * The count of measurement nodes contained in the subtree rooted at this
node. The counter is
+ * maintained in memory only.
+ */
+ long getSubtreeMeasurementCount();
+
+ void setSubtreeMeasurementCount(long subtreeMeasurementCount);
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/basic/BasicMNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/basic/BasicMNode.java
index a033c56c67e..2eab69749fd 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/basic/BasicMNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/basic/BasicMNode.java
@@ -46,6 +46,9 @@ public class BasicMNode implements IMemMNode {
private IMemMNode parent;
private final BasicMNodeInfo basicMNodeInfo;
+ /** Cached count of measurements in this node's subtree, rebuilt on restart.
*/
+ private long subtreeMeasurementCount = 0L;
+
/** from root to this node, only be set when used once for InternalMNode */
private String fullPath;
@@ -99,6 +102,16 @@ public class BasicMNode implements IMemMNode {
this.fullPath = fullPath;
}
+ @Override
+ public long getSubtreeMeasurementCount() {
+ return subtreeMeasurementCount;
+ }
+
+ @Override
+ public void setSubtreeMeasurementCount(final long subtreeMeasurementCount) {
+ this.subtreeMeasurementCount = subtreeMeasurementCount;
+ }
+
@Override
public PartialPath getPartialPath() {
final List<String> detachedPath = new ArrayList<>();
@@ -225,6 +238,7 @@ public class BasicMNode implements IMemMNode {
* <li>basicMNodeInfo reference, 8B
* <li>parent reference, 8B
* <li>fullPath reference, 8B
+ * <li>subtreeMeasurementCount, 8B
* </ol>
* <li>MapEntry in parent
* <ol>
@@ -236,7 +250,7 @@ public class BasicMNode implements IMemMNode {
*/
@Override
public int estimateSize() {
- return 8 + 8 + 8 + 8 + 8 + 8 + 28 + basicMNodeInfo.estimateSize();
+ return 8 + 8 + 8 + 8 + 8 + 8 + 8 + 28 + basicMNodeInfo.estimateSize();
}
@Override
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/AboveDatabaseMNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/AboveDatabaseMNode.java
index cff30d8b8c4..87144d4954a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/AboveDatabaseMNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/AboveDatabaseMNode.java
@@ -33,4 +33,14 @@ public class AboveDatabaseMNode extends
AbstractAboveDatabaseMNode<IMemMNode, Ba
public IMemMNode getAsMNode() {
return this;
}
+
+ @Override
+ public long getSubtreeMeasurementCount() {
+ return basicMNode.getSubtreeMeasurementCount();
+ }
+
+ @Override
+ public void setSubtreeMeasurementCount(long subtreeMeasurementCount) {
+ basicMNode.setSubtreeMeasurementCount(subtreeMeasurementCount);
+ }
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/DatabaseMNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/DatabaseMNode.java
index c6b2f5e2427..290cf427360 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/DatabaseMNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/DatabaseMNode.java
@@ -45,4 +45,14 @@ public class DatabaseMNode extends
AbstractDatabaseMNode<IMemMNode, BasicInterna
public void setDeviceInfo(IDeviceInfo<IMemMNode> deviceInfo) {
basicMNode.setDeviceInfo(deviceInfo);
}
+
+ @Override
+ public long getSubtreeMeasurementCount() {
+ return basicMNode.getSubtreeMeasurementCount();
+ }
+
+ @Override
+ public void setSubtreeMeasurementCount(long subtreeMeasurementCount) {
+ basicMNode.setSubtreeMeasurementCount(subtreeMeasurementCount);
+ }
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/MeasurementMNode.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/MeasurementMNode.java
index a40cbe6bc0f..d2a2cbd80c9 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/MeasurementMNode.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/mem/mnode/impl/MeasurementMNode.java
@@ -52,4 +52,14 @@ public class MeasurementMNode extends
AbstractMeasurementMNode<IMemMNode, BasicM
public final boolean isLogicalView() {
return false;
}
+
+ @Override
+ public long getSubtreeMeasurementCount() {
+ return basicMNode.getSubtreeMeasurementCount();
+ }
+
+ @Override
+ public void setSubtreeMeasurementCount(long subtreeMeasurementCount) {
+ basicMNode.setSubtreeMeasurementCount(subtreeMeasurementCount);
+ }
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java
index 8952f9d6cfc..8ca06b83f7d 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/mtree/impl/pbtree/MTreeBelowSGCachedImpl.java
@@ -42,6 +42,7 @@ import
org.apache.iotdb.db.exception.metadata.PathNotExistException;
import
org.apache.iotdb.db.exception.metadata.template.DifferentTemplateException;
import
org.apache.iotdb.db.exception.metadata.template.TemplateIsInUseException;
import org.apache.iotdb.db.queryengine.common.schematree.ClusterSchemaTree;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import org.apache.iotdb.db.schemaengine.metric.SchemaRegionCachedMetric;
import org.apache.iotdb.db.schemaengine.rescon.CachedSchemaRegionStatistics;
import
org.apache.iotdb.db.schemaengine.schemaregion.mtree.impl.pbtree.mnode.ICachedMNode;
@@ -64,6 +65,7 @@ import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.impl.Timeser
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.impl.SchemaReaderLimitOffsetWrapper;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.impl.TimeseriesReaderWithViewFetch;
+import org.apache.iotdb.db.schemaengine.schemaregion.utils.MNodeUtils;
import org.apache.iotdb.db.schemaengine.schemaregion.utils.MetaFormatUtils;
import
org.apache.iotdb.db.schemaengine.schemaregion.utils.filter.DeviceFilterVisitor;
import org.apache.iotdb.db.schemaengine.template.ClusterTemplateManager;
@@ -83,8 +85,10 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
@@ -1506,6 +1510,150 @@ public class MTreeBelowSGCachedImpl {
}
};
}
+
+ @Override
+ protected Iterator<ICachedMNode> getChildrenIterator(final
ICachedMNode parent)
+ throws MetadataException {
+ Ordering timeseriesOrdering =
showTimeSeriesPlan.getTimeseriesOrdering();
+ if (timeseriesOrdering == null || timeseriesOrdering ==
Ordering.DESC) {
+ return super.getChildrenIterator(parent);
+ }
+
+ final Iterator<ICachedMNode> templateIterator =
getSortedTemplateChildren(parent);
+ if (!templateIterator.hasNext()) {
+ return super.getChildrenIterator(parent);
+ }
+
+ return new IMNodeIterator<ICachedMNode>() {
+ private ICachedMNode next = null;
+ private ICachedMNode nextDirect = null;
+ private ICachedMNode nextTemplate = null;
+ private boolean skipTemplateChildren = false;
+ // Lazy init: avoid prefetching direct iterator before template
collection/sort.
+ private IMNodeIterator<ICachedMNode> directIterator = null;
+
+ @Override
+ public boolean hasNext() {
+ if (next != null) {
+ return true;
+ }
+ next = computeNext();
+ return next != null;
+ }
+
+ @Override
+ public ICachedMNode next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ICachedMNode result = next;
+ next = null;
+ return result;
+ }
+
+ @Override
+ public void skipTemplateChildren() {
+ skipTemplateChildren = true;
+ nextTemplate = null;
+ }
+
+ @Override
+ public void close() {
+ if (directIterator != null) {
+ directIterator.close();
+ }
+ }
+
+ private ICachedMNode computeNext() {
+ if (nextDirect == null) {
+ nextDirect = fetchNextDirect();
+ }
+ if (nextTemplate == null && !skipTemplateChildren) {
+ nextTemplate = fetchNextTemplate();
+ }
+
+ if (nextDirect == null && (skipTemplateChildren ||
nextTemplate == null)) {
+ return null;
+ }
+ if (nextDirect == null) {
+ ICachedMNode result = nextTemplate;
+ nextTemplate = null;
+ return result;
+ }
+ if (skipTemplateChildren || nextTemplate == null) {
+ ICachedMNode result = nextDirect;
+ nextDirect = null;
+ return result;
+ }
+
+ int cmp =
nextDirect.getName().compareTo(nextTemplate.getName());
+ if (cmp <= 0) {
+ ICachedMNode result = nextDirect;
+ nextDirect = null;
+ return result;
+ } else {
+ ICachedMNode result = nextTemplate;
+ nextTemplate = null;
+ return result;
+ }
+ }
+
+ private ICachedMNode fetchNextDirect() {
+ if (directIterator == null) {
+ try {
+ directIterator = store.getChildrenIterator(parent);
+ } catch (MetadataException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ while (directIterator.hasNext()) {
+ ICachedMNode node = directIterator.next();
+ if (!skipPreDeletedSchema
+ || !node.isMeasurement()
+ || !node.getAsMeasurementMNode().isPreDeleted()) {
+ return node;
+ }
+ }
+ return null;
+ }
+
+ private ICachedMNode fetchNextTemplate() {
+ while (templateIterator.hasNext()) {
+ ICachedMNode node = templateIterator.next();
+ if (!skipPreDeletedSchema
+ || !node.isMeasurement()
+ || !node.getAsMeasurementMNode().isPreDeleted()) {
+ return node;
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ private Iterator<ICachedMNode> getSortedTemplateChildren(final
ICachedMNode parent) {
+ if (templateMap == null || templateMap.isEmpty() ||
!parent.isDevice()) {
+ return Collections.emptyIterator();
+ }
+ final IDeviceMNode<ICachedMNode> deviceNode =
parent.getAsDeviceMNode();
+ if (deviceNode.getSchemaTemplateId() ==
SchemaConstant.NON_TEMPLATE) {
+ return Collections.emptyIterator();
+ }
+ if (skipPreDeletedSchema &&
deviceNode.isPreDeactivateSelfOrTemplate()) {
+ return Collections.emptyIterator();
+ }
+ final Template template =
templateMap.get(deviceNode.getSchemaTemplateId());
+ if (template == null) {
+ return Collections.emptyIterator();
+ }
+ final List<ICachedMNode> children = new ArrayList<>();
+ final Iterator<ICachedMNode> iterator =
MNodeUtils.getChildren(template, nodeFactory);
+ while (iterator.hasNext()) {
+ children.add(iterator.next());
+ }
+ children.sort(Comparator.comparing(ICachedMNode::getName));
+ return children.iterator();
+ }
};
collector.setTemplateMap(showTimeSeriesPlan.getRelatedTemplate(),
nodeFactory);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/IShowTimeSeriesPlan.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/IShowTimeSeriesPlan.java
index e05ee54f253..81612e2f7c7 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/IShowTimeSeriesPlan.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/IShowTimeSeriesPlan.java
@@ -22,6 +22,7 @@ package
org.apache.iotdb.db.schemaengine.schemaregion.read.req;
import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.commons.schema.template.Template;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import java.util.Map;
@@ -32,4 +33,7 @@ public interface IShowTimeSeriesPlan extends IShowSchemaPlan {
SchemaFilter getSchemaFilter();
Map<Integer, Template> getRelatedTemplate();
+
+ /** Ordering of timeseries full path in this region, null means no ordering.
*/
+ Ordering getTimeseriesOrdering();
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/SchemaRegionReadPlanFactory.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/SchemaRegionReadPlanFactory.java
index 84a2ca9cdf7..4fe59c58e7a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/SchemaRegionReadPlanFactory.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/SchemaRegionReadPlanFactory.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.commons.schema.template.Template;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.req.impl.ShowDevicesPlanImpl;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.req.impl.ShowNodesPlanImpl;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.req.impl.ShowTimeSeriesPlanImpl;
@@ -61,9 +62,18 @@ public class SchemaRegionReadPlanFactory {
boolean isPrefixMatch,
SchemaFilter schemaFilter,
boolean needViewDetail,
- PathPatternTree scope) {
+ PathPatternTree scope,
+ Ordering timeseriesOrdering) {
return new ShowTimeSeriesPlanImpl(
- path, relatedTemplate, limit, offset, isPrefixMatch, schemaFilter,
needViewDetail, scope);
+ path,
+ relatedTemplate,
+ limit,
+ offset,
+ isPrefixMatch,
+ schemaFilter,
+ needViewDetail,
+ scope,
+ timeseriesOrdering);
}
public static IShowNodesPlan getShowNodesPlan(PartialPath path,
PathPatternTree scope) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/impl/ShowTimeSeriesPlanImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/impl/ShowTimeSeriesPlanImpl.java
index 8aeb0e837be..7bb5aa9d91c 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/impl/ShowTimeSeriesPlanImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/req/impl/ShowTimeSeriesPlanImpl.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.commons.schema.filter.SchemaFilter;
import org.apache.iotdb.commons.schema.template.Template;
+import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
import
org.apache.iotdb.db.schemaengine.schemaregion.read.req.IShowTimeSeriesPlan;
import java.util.Map;
@@ -37,6 +38,9 @@ public class ShowTimeSeriesPlanImpl extends
AbstractShowSchemaPlanImpl
private final SchemaFilter schemaFilter;
private final boolean needViewDetail;
+ // order-by-timeseries pushdown ordering inside a single SchemaRegion
+ private final Ordering timeseriesOrdering;
+
public ShowTimeSeriesPlanImpl(
PartialPath path,
Map<Integer, Template> relatedTemplate,
@@ -45,11 +49,13 @@ public class ShowTimeSeriesPlanImpl extends
AbstractShowSchemaPlanImpl
boolean isPrefixMatch,
SchemaFilter schemaFilter,
boolean needViewDetail,
- PathPatternTree scope) {
+ PathPatternTree scope,
+ Ordering timeseriesOrdering) {
super(path, limit, offset, isPrefixMatch, scope);
this.relatedTemplate = relatedTemplate;
this.schemaFilter = schemaFilter;
this.needViewDetail = needViewDetail;
+ this.timeseriesOrdering = timeseriesOrdering;
}
@Override
@@ -67,18 +73,24 @@ public class ShowTimeSeriesPlanImpl extends
AbstractShowSchemaPlanImpl
return relatedTemplate;
}
+ @Override
+ public Ordering getTimeseriesOrdering() {
+ return timeseriesOrdering;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ShowTimeSeriesPlanImpl that = (ShowTimeSeriesPlanImpl) o;
- return Objects.equals(relatedTemplate, that.relatedTemplate)
+ return Objects.equals(timeseriesOrdering, that.timeseriesOrdering)
+ && Objects.equals(relatedTemplate, that.relatedTemplate)
&& Objects.equals(schemaFilter, that.schemaFilter);
}
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), relatedTemplate, schemaFilter);
+ return Objects.hash(super.hashCode(), relatedTemplate, schemaFilter,
timeseriesOrdering);
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java
index f35d047753f..04f4ca8b6f9 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/schemaregion/read/resp/reader/impl/SchemaReaderLimitOffsetWrapper.java
@@ -45,7 +45,7 @@ public class SchemaReaderLimitOffsetWrapper<T extends
ISchemaInfo> implements IS
this.schemaReader = schemaReader;
this.limit = limit;
this.offset = offset;
- this.hasLimit = limit > 0 || offset > 0;
+ this.hasLimit = limit > 0;
}
@Override
@@ -73,24 +73,20 @@ public class SchemaReaderLimitOffsetWrapper<T extends
ISchemaInfo> implements IS
}
private ListenableFuture<?> tryGetNext() {
- if (hasLimit) {
- if (curOffset < offset) {
- // first time
- return Futures.submit(
- () -> {
- while (curOffset < offset && schemaReader.hasNext()) {
- schemaReader.next();
- curOffset++;
- }
- return schemaReader.hasNext();
- },
- directExecutor());
- }
- if (count >= limit) {
- return NOT_BLOCKED;
- } else {
- return schemaReader.isBlocked();
- }
+ if (curOffset < offset) {
+ // first time
+ return Futures.submit(
+ () -> {
+ while (curOffset < offset && schemaReader.hasNext()) {
+ schemaReader.next();
+ curOffset++;
+ }
+ return schemaReader.hasNext();
+ },
+ directExecutor());
+ }
+ if (hasLimit && count >= limit) {
+ return NOT_BLOCKED;
} else {
return schemaReader.isBlocked();
}
@@ -101,7 +97,7 @@ public class SchemaReaderLimitOffsetWrapper<T extends
ISchemaInfo> implements IS
public boolean hasNext() {
try {
isBlocked().get();
- return schemaReader.hasNext() && (limit == 0 || count < limit);
+ return schemaReader.hasNext() && (!hasLimit || count < limit);
} catch (Exception e) {
throw new RuntimeException(e);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
index 857db657077..41fb89efcc5 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java
@@ -618,10 +618,14 @@ public class ClusterTemplateManager implements
ITemplateManager {
}
public void updateTemplateInfo(byte[] templateInfo) {
+ Template template =
+
TemplateInternalRPCUtil.parseUpdateTemplateInfoBytes(ByteBuffer.wrap(templateInfo));
+ updateTemplateInfo(template);
+ }
+
+ public void updateTemplateInfo(Template template) {
readWriteLock.writeLock().lock();
try {
- Template template =
-
TemplateInternalRPCUtil.parseUpdateTemplateInfoBytes(ByteBuffer.wrap(templateInfo));
templateIdMap.put(template.getId(), template);
} finally {
readWriteLock.writeLock().unlock();
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTestUtil.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTestUtil.java
index 42800c4e586..8a24133d453 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTestUtil.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/metadata/schemaRegion/SchemaRegionTestUtil.java
@@ -175,7 +175,15 @@ public class SchemaRegionTestUtil {
try (ISchemaReader<ITimeSeriesSchemaInfo> timeSeriesReader =
schemaRegion.getTimeSeriesReader(
SchemaRegionReadPlanFactory.getShowTimeSeriesPlan(
- pathPattern, templateMap, 0, 0, isPrefixMatch, null, false,
ALL_MATCH_SCOPE))) {
+ pathPattern,
+ templateMap,
+ 0,
+ 0,
+ isPrefixMatch,
+ null,
+ false,
+ ALL_MATCH_SCOPE,
+ null))) {
long count = 0;
while (timeSeriesReader.hasNext()) {
timeSeriesReader.next();
@@ -200,7 +208,15 @@ public class SchemaRegionTestUtil {
try (final ISchemaReader<ITimeSeriesSchemaInfo> timeSeriesReader =
schemaRegion.getTimeSeriesReader(
SchemaRegionReadPlanFactory.getShowTimeSeriesPlan(
- pathPattern, Collections.emptyMap(), 0, 0, false, null, false,
ALL_MATCH_SCOPE))) {
+ pathPattern,
+ Collections.emptyMap(),
+ 0,
+ 0,
+ false,
+ null,
+ false,
+ ALL_MATCH_SCOPE,
+ null))) {
Assert.assertTrue(timeSeriesReader.hasNext());
final ITimeSeriesSchemaInfo info = timeSeriesReader.next();
Assert.assertEquals(isAligned, info.isUnderAlignedDevice());
@@ -237,7 +253,7 @@ public class SchemaRegionTestUtil {
try (ISchemaReader<ITimeSeriesSchemaInfo> timeSeriesReader =
schemaRegion.getTimeSeriesReader(
SchemaRegionReadPlanFactory.getShowTimeSeriesPlan(
- pathPattern, null, 0, 0, isPrefixMatch, null, false,
ALL_MATCH_SCOPE))) {
+ pathPattern, null, 0, 0, isPrefixMatch, null, false,
ALL_MATCH_SCOPE, null))) {
Map<PartialPath, Long> countMap = new HashMap<>();
while (timeSeriesReader.hasNext()) {
ITimeSeriesSchemaInfo timeSeriesSchemaInfo = timeSeriesReader.next();
@@ -356,7 +372,8 @@ public class SchemaRegionTestUtil {
isPrefixMatch,
schemaFilter,
needViewDetail,
- ALL_MATCH_SCOPE))) {
+ ALL_MATCH_SCOPE,
+ null))) {
while (reader.hasNext()) {
timeSeriesSchemaInfo = reader.next();
result.add(
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperatorTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperatorTest.java
index a5d4a6e6ace..75d450430ad 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperatorTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaQueryScanOperatorTest.java
@@ -215,7 +215,8 @@ public class SchemaQueryScanOperatorTest {
0,
null,
Collections.emptyMap(),
- SchemaConstant.ALL_MATCH_SCOPE);
+ SchemaConstant.ALL_MATCH_SCOPE,
+ null);
SchemaOperatorTestUtil.mockGetSchemaReader(
timeSeriesSchemaSource, showTimeSeriesResults.iterator(),
schemaRegion, true);