This is an automated email from the ASF dual-hosted git repository. Caideyipi pushed a commit to branch to-rc/2010 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 3c45f43308c67ed3a88cb98754246a50f72b22b4 Author: Caideyipi <[email protected]> AuthorDate: Wed Jun 3 15:45:39 2026 +0800 Fix count and show timeseries follow-up (#17804) * Fix count and show timeseries follow-up * Refine internal timeseries count permissions * fix --- .../regionscan/IoTDBActiveSchemaQueryIT.java | 47 ++++ .../impl/DataNodeInternalRPCServiceImpl.java | 6 +- .../db/queryengine/common/TimeseriesContext.java | 294 ++++++++++++++++++++- .../schema/CountGroupByLevelScanOperator.java | 15 +- .../operator/schema/SchemaCountOperator.java | 4 + .../operator/schema/source/ISchemaSource.java | 4 + .../schema/source/SchemaSourceFactory.java | 18 +- .../schema/source/TimeSeriesSchemaSource.java | 20 ++ .../source/ActiveTimeSeriesRegionScanOperator.java | 92 +++++-- .../queryengine/plan/analyze/AnalyzeVisitor.java | 189 +++++++++++-- .../plan/planner/LogicalPlanBuilder.java | 16 +- .../plan/planner/LogicalPlanVisitor.java | 8 +- .../plan/planner/OperatorTreeGenerator.java | 8 +- .../plan/planner/distribution/SourceRewriter.java | 10 +- .../metadata/read/LevelTimeSeriesCountNode.java | 48 +++- .../node/metadata/read/TimeSeriesCountNode.java | 49 +++- .../plan/node/source/TimeseriesRegionScanNode.java | 7 + .../security/TreeAccessCheckVisitor.java | 99 ++++++- .../plan/statement/metadata/CountStatement.java | 9 + .../org/apache/iotdb/db/auth/TreeAccessTest.java | 133 ++++++++++ .../schema/source/TimeSeriesSchemaSourceTest.java | 183 +++++++++++++ .../distribution/RegionScanPlanningTest.java | 81 ++++++ .../logical/RegionScanLogicalPlannerTest.java | 47 ++++ .../metadata/read/SchemaCountNodeSerdeTest.java | 8 +- 24 files changed, 1312 insertions(+), 83 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/regionscan/IoTDBActiveSchemaQueryIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/regionscan/IoTDBActiveSchemaQueryIT.java index c0e7673d7d6..1361f360202 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/schema/regionscan/IoTDBActiveSchemaQueryIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/schema/regionscan/IoTDBActiveSchemaQueryIT.java @@ -27,6 +27,7 @@ import org.apache.iotdb.util.AbstractSchemaIT; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runners.Parameterized; @@ -236,6 +237,52 @@ public class IoTDBActiveSchemaQueryIT extends AbstractSchemaIT { } } + @Test + @Ignore + public void testCountTimeSeriesWithTimeConditionIncludesView() { + try (Connection connection = EnvFactory.getEnv().getConnection(); + Statement statement = connection.createStatement()) { + statement.execute("CREATE DATABASE root.view_count"); + statement.execute( + "CREATE TIMESERIES root.view_count.src.s1 WITH DATATYPE = INT32, ENCODING = PLAIN"); + statement.execute( + "CREATE TIMESERIES root.view_count.src.s2 WITH DATATYPE = INT32, ENCODING = PLAIN"); + statement.execute("CREATE VIEW root.view_count.dst.v1 AS SELECT s1 FROM root.view_count.src"); + + checkResultSet( + statement, + "count timeseries root.view_count.**", + new HashSet<>(Collections.singletonList("3,"))); + + statement.execute("insert into root.view_count.src(timestamp,s1) values(1,1)"); + + checkResultSet( + statement, + "count timeseries root.view_count.** where time>0", + new HashSet<>(Collections.singletonList("2,"))); + checkResultSet( + statement, + "show timeseries root.view_count.** where time>0", + new HashSet<>( + Arrays.asList( + "root.view_count.src.s1,null,root.view_count,INT32,PLAIN,LZ4,null,null,null,null,BASE,", + "root.view_count.dst.v1,null,root.view_count,INT32,null,null,null,null,null,null,VIEW,"))); + checkResultSet( + statement, + "count timeseries root.view_count.dst.** where time>0", + new HashSet<>(Collections.singletonList("1,"))); + checkResultSet( + statement, + "show timeseries root.view_count.dst.** where time>0", + new HashSet<>( + Collections.singletonList( + "root.view_count.dst.v1,null,root.view_count,INT32,null,null,null,null,null,null,VIEW,"))); + } catch (Exception e) { + e.printStackTrace(); + Assert.fail(e.getMessage()); + } + } + @Test public void testShowDevices() { try (Connection connection = EnvFactory.getEnv().getConnection(); 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 e5c99640564..ebef68b0486 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 @@ -1153,7 +1153,7 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface for (final PartialPath pattern : filteredPatternTree.getAllPathPatterns()) { ISchemaSource<ITimeSeriesSchemaInfo> schemaSource = SchemaSourceFactory.getTimeSeriesSchemaCountSource( - pattern, false, null, null, SchemaConstant.ALL_MATCH_SCOPE); + pattern, false, null, null, SchemaConstant.ALL_MATCH_SCOPE, true, true); try (final ISchemaReader<ITimeSeriesSchemaInfo> schemaReader = schemaSource.getSchemaReader(schemaRegion)) { if (schemaReader.hasNext()) { @@ -1996,7 +1996,9 @@ public class DataNodeInternalRPCServiceImpl implements IDataNodeRPCService.Iface // Does not support logical view currently SchemaFilterFactory.createViewTypeFilter(ViewType.BASE), null, - SchemaConstant.ALL_MATCH_SCOPE); + SchemaConstant.ALL_MATCH_SCOPE, + true, + true); try (final ISchemaReader<ITimeSeriesSchemaInfo> schemaReader = schemaSource.getSchemaReader(schemaRegion)) { final Map<String, Byte> updateMap = resp.getDeviewViewFieldTypeMap(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/TimeseriesContext.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/TimeseriesContext.java index 9225b97fc70..ecad26bac00 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/TimeseriesContext.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/TimeseriesContext.java @@ -28,7 +28,11 @@ import org.apache.tsfile.utils.ReadWriteIOUtils; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Objects; +import java.util.Set; import static org.apache.iotdb.db.queryengine.execution.operator.schema.source.TimeSeriesSchemaSource.mapToString; @@ -42,11 +46,70 @@ public class TimeseriesContext { private final String deadband; private final String deadbandParameters; + private final String database; + private final int activeCountMultiplier; + private final boolean logicalView; + private final Map<String, TimeseriesContext> activeLogicalViewContextMap; public TimeseriesContext(IMeasurementSchemaInfo schemaInfo) { + this(schemaInfo, 1, Collections.emptyMap()); + } + + public TimeseriesContext( + IMeasurementSchemaInfo schemaInfo, + int activeCountMultiplier, + Set<String> activeLogicalViewCountSet) { + this(schemaInfo, activeCountMultiplier, createLogicalViewContextMap(activeLogicalViewCountSet)); + } + + public TimeseriesContext( + IMeasurementSchemaInfo schemaInfo, + int activeCountMultiplier, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { this.dataType = schemaInfo.getSchema().getType().toString(); - this.encoding = schemaInfo.getSchema().getEncodingType().toString(); - this.compression = schemaInfo.getSchema().getCompressor().toString(); + this.logicalView = schemaInfo.isLogicalView(); + if (logicalView) { + this.encoding = null; + this.compression = null; + } else { + this.encoding = schemaInfo.getSchema().getEncodingType().toString(); + this.compression = schemaInfo.getSchema().getCompressor().toString(); + } + this.alias = schemaInfo.getAlias(); + this.tags = mapToString(schemaInfo.getTagMap()); + this.attributes = mapToString(schemaInfo.getAttributeMap()); + Pair<String, String> deadbandInfo = + MetaUtils.parseDeadbandInfo(schemaInfo.getSchema().getProps()); + this.deadband = deadbandInfo.left; + this.deadbandParameters = deadbandInfo.right; + this.database = null; + this.activeCountMultiplier = activeCountMultiplier; + this.activeLogicalViewContextMap = new HashMap<>(activeLogicalViewContextMap); + } + + public TimeseriesContext( + IMeasurementSchemaInfo schemaInfo, + String dataType, + int activeCountMultiplier, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { + this(schemaInfo, dataType, null, activeCountMultiplier, activeLogicalViewContextMap); + } + + public TimeseriesContext( + IMeasurementSchemaInfo schemaInfo, + String dataType, + String database, + int activeCountMultiplier, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { + this.dataType = dataType; + this.logicalView = schemaInfo.isLogicalView(); + if (logicalView) { + this.encoding = null; + this.compression = null; + } else { + this.encoding = schemaInfo.getSchema().getEncodingType().toString(); + this.compression = schemaInfo.getSchema().getCompressor().toString(); + } this.alias = schemaInfo.getAlias(); this.tags = mapToString(schemaInfo.getTagMap()); this.attributes = mapToString(schemaInfo.getAttributeMap()); @@ -54,6 +117,9 @@ public class TimeseriesContext { MetaUtils.parseDeadbandInfo(schemaInfo.getSchema().getProps()); this.deadband = deadbandInfo.left; this.deadbandParameters = deadbandInfo.right; + this.database = database; + this.activeCountMultiplier = activeCountMultiplier; + this.activeLogicalViewContextMap = new HashMap<>(activeLogicalViewContextMap); } public String getDataType() { @@ -88,6 +154,26 @@ public class TimeseriesContext { return deadband; } + public String getDatabase() { + return database; + } + + public int getActiveCountMultiplier() { + return activeCountMultiplier; + } + + public Set<String> getActiveLogicalViewCountSet() { + return activeLogicalViewContextMap.keySet(); + } + + public Map<String, TimeseriesContext> getActiveLogicalViewContextMap() { + return activeLogicalViewContextMap; + } + + public boolean isLogicalView() { + return logicalView; + } + public TimeseriesContext( String dataType, String alias, @@ -97,6 +183,113 @@ public class TimeseriesContext { String attributes, String deadband, String deadbandParameters) { + this( + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + 1, + false, + null, + Collections.emptyMap()); + } + + public TimeseriesContext( + String dataType, + String alias, + String encoding, + String compression, + String tags, + String attributes, + String deadband, + String deadbandParameters, + int activeCountMultiplier, + Set<String> activeLogicalViewCountSet) { + this( + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + activeCountMultiplier, + false, + null, + createLogicalViewContextMap(activeLogicalViewCountSet)); + } + + public TimeseriesContext( + String dataType, + String alias, + String encoding, + String compression, + String tags, + String attributes, + String deadband, + String deadbandParameters, + int activeCountMultiplier, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { + this( + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + activeCountMultiplier, + false, + null, + activeLogicalViewContextMap); + } + + public TimeseriesContext( + String dataType, + String alias, + String encoding, + String compression, + String tags, + String attributes, + String deadband, + String deadbandParameters, + int activeCountMultiplier, + boolean logicalView, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { + this( + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + activeCountMultiplier, + logicalView, + null, + activeLogicalViewContextMap); + } + + public TimeseriesContext( + String dataType, + String alias, + String encoding, + String compression, + String tags, + String attributes, + String deadband, + String deadbandParameters, + int activeCountMultiplier, + boolean logicalView, + String database, + Map<String, TimeseriesContext> activeLogicalViewContextMap) { this.dataType = dataType; this.alias = alias; this.encoding = encoding; @@ -105,6 +298,44 @@ public class TimeseriesContext { this.attributes = attributes; this.deadband = deadband; this.deadbandParameters = deadbandParameters; + this.database = database; + this.activeCountMultiplier = activeCountMultiplier; + this.logicalView = logicalView; + this.activeLogicalViewContextMap = new HashMap<>(activeLogicalViewContextMap); + } + + private static Map<String, TimeseriesContext> createLogicalViewContextMap( + Set<String> activeLogicalViewCountSet) { + if (activeLogicalViewCountSet.isEmpty()) { + return Collections.emptyMap(); + } + Map<String, TimeseriesContext> activeLogicalViewContextMap = new HashMap<>(); + for (String logicalView : activeLogicalViewCountSet) { + activeLogicalViewContextMap.put( + logicalView, + new TimeseriesContext( + null, null, null, null, null, null, null, null, 1, true, Collections.emptyMap())); + } + return activeLogicalViewContextMap; + } + + public TimeseriesContext mergeActiveCount(TimeseriesContext that) { + Map<String, TimeseriesContext> mergedActiveLogicalViewContextMap = + new HashMap<>(activeLogicalViewContextMap); + mergedActiveLogicalViewContextMap.putAll(that.activeLogicalViewContextMap); + return new TimeseriesContext( + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + activeCountMultiplier + that.activeCountMultiplier, + logicalView, + database, + mergedActiveLogicalViewContextMap); } public void serializeAttributes(ByteBuffer byteBuffer) { @@ -116,6 +347,14 @@ public class TimeseriesContext { ReadWriteIOUtils.write(attributes, byteBuffer); ReadWriteIOUtils.write(deadband, byteBuffer); ReadWriteIOUtils.write(deadbandParameters, byteBuffer); + ReadWriteIOUtils.write(database, byteBuffer); + ReadWriteIOUtils.write(activeCountMultiplier, byteBuffer); + ReadWriteIOUtils.write(logicalView, byteBuffer); + ReadWriteIOUtils.write(activeLogicalViewContextMap.size(), byteBuffer); + for (Map.Entry<String, TimeseriesContext> entry : activeLogicalViewContextMap.entrySet()) { + ReadWriteIOUtils.write(entry.getKey(), byteBuffer); + entry.getValue().serializeAttributes(byteBuffer); + } } public void serializeAttributes(DataOutputStream stream) throws IOException { @@ -127,6 +366,14 @@ public class TimeseriesContext { ReadWriteIOUtils.write(attributes, stream); ReadWriteIOUtils.write(deadband, stream); ReadWriteIOUtils.write(deadbandParameters, stream); + ReadWriteIOUtils.write(database, stream); + ReadWriteIOUtils.write(activeCountMultiplier, stream); + ReadWriteIOUtils.write(logicalView, stream); + ReadWriteIOUtils.write(activeLogicalViewContextMap.size(), stream); + for (Map.Entry<String, TimeseriesContext> entry : activeLogicalViewContextMap.entrySet()) { + ReadWriteIOUtils.write(entry.getKey(), stream); + entry.getValue().serializeAttributes(stream); + } } public static TimeseriesContext deserialize(ByteBuffer buffer) { @@ -138,8 +385,28 @@ public class TimeseriesContext { String attributes = ReadWriteIOUtils.readString(buffer); String deadband = ReadWriteIOUtils.readString(buffer); String deadbandParameters = ReadWriteIOUtils.readString(buffer); + String database = ReadWriteIOUtils.readString(buffer); + int activeCountMultiplier = ReadWriteIOUtils.readInt(buffer); + boolean logicalView = ReadWriteIOUtils.readBool(buffer); + int activeLogicalViewContextMapSize = ReadWriteIOUtils.readInt(buffer); + Map<String, TimeseriesContext> activeLogicalViewContextMap = new HashMap<>(); + for (int i = 0; i < activeLogicalViewContextMapSize; i++) { + activeLogicalViewContextMap.put( + ReadWriteIOUtils.readString(buffer), TimeseriesContext.deserialize(buffer)); + } return new TimeseriesContext( - dataType, alias, encoding, compression, tags, attributes, deadband, deadbandParameters); + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + activeCountMultiplier, + logicalView, + database, + activeLogicalViewContextMap); } @Override @@ -154,18 +421,33 @@ public class TimeseriesContext { boolean res = Objects.equals(dataType, that.dataType) && Objects.equals(alias, that.alias) - && encoding.equals(that.encoding) + && Objects.equals(encoding, that.encoding) && Objects.equals(compression, that.compression) && Objects.equals(tags, that.tags) && Objects.equals(attributes, that.attributes) && Objects.equals(deadband, that.deadband) - && Objects.equals(deadbandParameters, that.deadbandParameters); + && Objects.equals(deadbandParameters, that.deadbandParameters) + && Objects.equals(database, that.database) + && activeCountMultiplier == that.activeCountMultiplier + && logicalView == that.logicalView + && Objects.equals(activeLogicalViewContextMap, that.activeLogicalViewContextMap); return res; } @Override public int hashCode() { return Objects.hash( - dataType, alias, encoding, compression, tags, attributes, deadband, deadbandParameters); + dataType, + alias, + encoding, + compression, + tags, + attributes, + deadband, + deadbandParameters, + database, + activeCountMultiplier, + logicalView, + activeLogicalViewContextMap); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountGroupByLevelScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountGroupByLevelScanOperator.java index 69f36680fb7..35c57dafa4d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountGroupByLevelScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/CountGroupByLevelScanOperator.java @@ -27,6 +27,7 @@ import org.apache.iotdb.db.queryengine.execution.driver.SchemaDriverContext; import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; import org.apache.iotdb.db.queryengine.execution.operator.schema.source.ISchemaSource; import org.apache.iotdb.db.queryengine.execution.operator.source.SourceOperator; +import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ISchemaInfo; import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.reader.ISchemaReader; @@ -95,6 +96,10 @@ public class CountGroupByLevelScanOperator<T extends ISchemaInfo> implements Sou return operatorContext; } + private ISchemaRegion getSchemaRegion() { + return ((SchemaDriverContext) operatorContext.getDriverContext()).getSchemaRegion(); + } + @Override public ListenableFuture<?> isBlocked() { if (isBlocked == null) { @@ -109,6 +114,11 @@ public class CountGroupByLevelScanOperator<T extends ISchemaInfo> implements Sou */ private ListenableFuture<?> tryGetNext() { if (schemaReader == null) { + if (schemaSource.shouldSkipSchemaRegion(getSchemaRegion())) { + next = null; + isFinished = true; + return NOT_BLOCKED; + } schemaReader = createTimeSeriesReader(); } while (true) { @@ -172,15 +182,14 @@ public class CountGroupByLevelScanOperator<T extends ISchemaInfo> implements Sou @Override public boolean hasNext() throws Exception { isBlocked().get(); // wait for the next TsBlock - if (!schemaReader.isSuccess()) { + if (schemaReader != null && !schemaReader.isSuccess()) { throw new SchemaExecutionException(schemaReader.getFailure()); } return next != null; } public ISchemaReader<T> createTimeSeriesReader() { - return schemaSource.getSchemaReader( - ((SchemaDriverContext) operatorContext.getDriverContext()).getSchemaRegion()); + return schemaSource.getSchemaReader(getSchemaRegion()); } private TsBlock constructTsBlockAndClearMap(Map<PartialPath, Long> countMap) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java index 9b2884233c1..4cafe40c36b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/SchemaCountOperator.java @@ -100,6 +100,10 @@ public class SchemaCountOperator<T extends ISchemaInfo> implements SourceOperato */ private ListenableFuture<?> tryGetNext() { ISchemaRegion schemaRegion = getSchemaRegion(); + if (schemaSource.shouldSkipSchemaRegion(schemaRegion)) { + next = constructTsBlock(0); + return NOT_BLOCKED; + } if (schemaSource.hasSchemaStatistic(schemaRegion)) { long statisticCount = schemaSource.getSchemaStatistic(schemaRegion); // Check if database path itself is counted as a device (bug fix) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/ISchemaSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/ISchemaSource.java index 41bd5f3fbe0..4417203fdc8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/ISchemaSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/ISchemaSource.java @@ -57,6 +57,10 @@ public interface ISchemaSource<T extends ISchemaInfo> { long getSchemaStatistic(final ISchemaRegion schemaRegion); + default boolean shouldSkipSchemaRegion(final ISchemaRegion schemaRegion) { + return false; + } + default boolean checkRegionDatabaseIncluded(final ISchemaRegion schemaRegion) { return true; } 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 87560ad47be..85bc9bcb77c 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 @@ -46,9 +46,21 @@ public class SchemaSourceFactory { boolean isPrefixMatch, SchemaFilter schemaFilter, Map<Integer, Template> templateMap, - PathPatternTree scope) { + PathPatternTree scope, + boolean includeSystemDatabase, + boolean includeAuditDatabase) { return new TimeSeriesSchemaSource( - pathPattern, isPrefixMatch, 0, 0, schemaFilter, templateMap, false, scope, null); + pathPattern, + isPrefixMatch, + 0, + 0, + schemaFilter, + templateMap, + false, + includeSystemDatabase, + includeAuditDatabase, + scope, + null); } // show time series @@ -69,6 +81,8 @@ public class SchemaSourceFactory { schemaFilter, templateMap, true, + true, + true, scope, timeseriesOrdering); } 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 a56cfa228bf..0b89998052b 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 @@ -27,6 +27,7 @@ import org.apache.iotdb.commons.schema.SchemaConstant; import org.apache.iotdb.commons.schema.column.ColumnHeader; import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.commons.schema.filter.SchemaFilter; +import org.apache.iotdb.commons.schema.table.Audit; 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; @@ -55,6 +56,8 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn private final SchemaFilter schemaFilter; private final Map<Integer, Template> templateMap; private final boolean needViewDetail; + private final boolean includeSystemDatabase; + private final boolean includeAuditDatabase; private final Ordering timeseriesOrdering; TimeSeriesSchemaSource( @@ -65,6 +68,8 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn SchemaFilter schemaFilter, Map<Integer, Template> templateMap, boolean needViewDetail, + boolean includeSystemDatabase, + boolean includeAuditDatabase, PathPatternTree scope, Ordering timeseriesOrdering) { this.pathPattern = pathPattern; @@ -74,6 +79,8 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn this.schemaFilter = schemaFilter; this.templateMap = templateMap; this.needViewDetail = needViewDetail; + this.includeSystemDatabase = includeSystemDatabase; + this.includeAuditDatabase = includeAuditDatabase; this.scope = scope; this.timeseriesOrdering = timeseriesOrdering; } @@ -141,6 +148,19 @@ public class TimeSeriesSchemaSource implements ISchemaSource<ITimeSeriesSchemaIn return schemaRegion.getSchemaRegionStatistics().getSeriesNumber(true); } + @Override + public boolean shouldSkipSchemaRegion(final ISchemaRegion schemaRegion) { + final String database = schemaRegion.getDatabaseFullPath(); + if (SchemaConstant.SYSTEM_DATABASE.equals(database)) { + return !includeSystemDatabase; + } + if (SchemaConstant.AUDIT_DATABASE.equals(database) + || Audit.TABLE_MODEL_AUDIT_DATABASE.equals(database)) { + return !includeAuditDatabase; + } + return false; + } + public static String mapToString(Map<String, String> map) { if (map == null || map.isEmpty()) { return null; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ActiveTimeSeriesRegionScanOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ActiveTimeSeriesRegionScanOperator.java index b6ac9ecd850..e61de2818b6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ActiveTimeSeriesRegionScanOperator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/ActiveTimeSeriesRegionScanOperator.java @@ -35,15 +35,22 @@ import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.RamUsageEstimator; import java.io.IOException; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; public class ActiveTimeSeriesRegionScanOperator extends AbstractRegionScanDataSourceOperator { // Timeseries which need to be checked. private final Map<IDeviceID, Map<String, TimeseriesContext>> timeSeriesToSchemasInfo; - private static final Binary VIEW_TYPE = new Binary("BASE".getBytes()); + private final Set<String> countedLogicalViews; + private static final Binary BASE_VIEW_TYPE = + new Binary("BASE".getBytes(TSFileConfig.STRING_CHARSET)); + private static final Binary LOGICAL_VIEW_TYPE = + new Binary("VIEW".getBytes(TSFileConfig.STRING_CHARSET)); private final Binary dataBaseName; + private final String dataBaseNameString; private static final long INSTANCE_SIZE = RamUsageEstimator.shallowSizeOfInstance(ActiveTimeSeriesRegionScanOperator.class) + RamUsageEstimator.shallowSizeOfInstance(Map.class) @@ -60,15 +67,15 @@ public class ActiveTimeSeriesRegionScanOperator extends AbstractRegionScanDataSo this.operatorContext = operatorContext; this.sourceId = sourceId; this.timeSeriesToSchemasInfo = timeSeriesToSchemasInfo; + this.countedLogicalViews = new HashSet<>(); this.regionScanUtil = new RegionScanForActiveTimeSeriesUtil(timeFilter, ttlCache); - this.dataBaseName = - new Binary( - operatorContext - .getDriverContext() - .getFragmentInstanceContext() - .getDataRegion() - .getDatabaseName() - .getBytes(TSFileConfig.STRING_CHARSET)); + this.dataBaseNameString = + operatorContext + .getDriverContext() + .getFragmentInstanceContext() + .getDataRegion() + .getDatabaseName(); + this.dataBaseName = new Binary(this.dataBaseNameString.getBytes(TSFileConfig.STRING_CHARSET)); } @Override @@ -92,16 +99,22 @@ public class ActiveTimeSeriesRegionScanOperator extends AbstractRegionScanDataSo @Override protected void updateActiveData() { - TimeColumnBuilder timeColumnBuilder = resultTsBlockBuilder.getTimeColumnBuilder(); - ColumnBuilder[] columnBuilders = resultTsBlockBuilder.getValueColumnBuilders(); - Map<IDeviceID, List<String>> activeTimeSeries = ((RegionScanForActiveTimeSeriesUtil) regionScanUtil).getActiveTimeSeries(); if (outputCount) { for (Map.Entry<IDeviceID, List<String>> entry : activeTimeSeries.entrySet()) { List<String> timeSeriesList = entry.getValue(); - count += timeSeriesList.size(); + Map<String, TimeseriesContext> timeSeriesInfo = timeSeriesToSchemasInfo.get(entry.getKey()); + for (String timeSeries : timeSeriesList) { + TimeseriesContext schemaInfo = timeSeriesInfo.get(timeSeries); + count += schemaInfo.getActiveCountMultiplier(); + for (String logicalView : schemaInfo.getActiveLogicalViewCountSet()) { + if (countedLogicalViews.add(logicalView)) { + count++; + } + } + } removeTimeseriesListFromDevice(entry.getKey(), timeSeriesList); } return; @@ -114,26 +127,49 @@ public class ActiveTimeSeriesRegionScanOperator extends AbstractRegionScanDataSo Map<String, TimeseriesContext> timeSeriesInfo = timeSeriesToSchemasInfo.get(deviceID); for (String timeSeries : timeSeriesList) { TimeseriesContext schemaInfo = timeSeriesInfo.get(timeSeries); - timeColumnBuilder.writeLong(-1); - columnBuilders[0].writeBinary( - new Binary(contactDeviceAndMeasurement(deviceStr, timeSeries))); - - checkAndAppend(schemaInfo.getAlias(), columnBuilders[1]); // Measurement - columnBuilders[2].writeBinary(dataBaseName); // Database - checkAndAppend(schemaInfo.getDataType(), columnBuilders[3]); // DataType - checkAndAppend(schemaInfo.getEncoding(), columnBuilders[4]); // Encoding - checkAndAppend(schemaInfo.getCompression(), columnBuilders[5]); // Compression - checkAndAppend(schemaInfo.getTags(), columnBuilders[6]); // Tags - checkAndAppend(schemaInfo.getAttributes(), columnBuilders[7]); // Attributes - checkAndAppend(schemaInfo.getDeadband(), columnBuilders[8]); // Description - checkAndAppend(schemaInfo.getDeadbandParameters(), columnBuilders[9]); // DeadbandParameters - columnBuilders[10].writeBinary(VIEW_TYPE); // ViewType - resultTsBlockBuilder.declarePosition(); + if (schemaInfo.getActiveCountMultiplier() > 0) { + appendTimeseries( + contactDeviceAndMeasurement(deviceStr, timeSeries), schemaInfo, BASE_VIEW_TYPE); + } + for (Map.Entry<String, TimeseriesContext> logicalViewEntry : + schemaInfo.getActiveLogicalViewContextMap().entrySet()) { + if (countedLogicalViews.add(logicalViewEntry.getKey())) { + appendTimeseries( + logicalViewEntry.getKey().getBytes(TSFileConfig.STRING_CHARSET), + logicalViewEntry.getValue(), + LOGICAL_VIEW_TYPE); + } + } } removeTimeseriesListFromDevice(deviceID, timeSeriesList); } } + private void appendTimeseries( + byte[] timeseriesPath, TimeseriesContext schemaInfo, Binary viewType) { + TimeColumnBuilder timeColumnBuilder = resultTsBlockBuilder.getTimeColumnBuilder(); + ColumnBuilder[] columnBuilders = resultTsBlockBuilder.getValueColumnBuilders(); + + timeColumnBuilder.writeLong(-1); + columnBuilders[0].writeBinary(new Binary(timeseriesPath)); + + checkAndAppend(schemaInfo.getAlias(), columnBuilders[1]); // Measurement + if (schemaInfo.getDatabase() == null || dataBaseNameString.equals(schemaInfo.getDatabase())) { + columnBuilders[2].writeBinary(dataBaseName); // Database + } else { + checkAndAppend(schemaInfo.getDatabase(), columnBuilders[2]); // Database + } + checkAndAppend(schemaInfo.getDataType(), columnBuilders[3]); // DataType + checkAndAppend(schemaInfo.getEncoding(), columnBuilders[4]); // Encoding + checkAndAppend(schemaInfo.getCompression(), columnBuilders[5]); // Compression + checkAndAppend(schemaInfo.getTags(), columnBuilders[6]); // Tags + checkAndAppend(schemaInfo.getAttributes(), columnBuilders[7]); // Attributes + checkAndAppend(schemaInfo.getDeadband(), columnBuilders[8]); // Description + checkAndAppend(schemaInfo.getDeadbandParameters(), columnBuilders[9]); // DeadbandParameters + columnBuilders[10].writeBinary(viewType); // ViewType + resultTsBlockBuilder.declarePosition(); + } + private void removeTimeseriesListFromDevice(IDeviceID deviceID, List<String> timeSeriesList) { Map<String, TimeseriesContext> timeSeriesInfo = timeSeriesToSchemasInfo.get(deviceID); for (String timeSeries : timeSeriesList) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java index 7aa8d1e8636..22d5b0518ac 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java @@ -35,8 +35,11 @@ import org.apache.iotdb.commons.path.AlignedPath; 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.queryengine.common.NodeRef; +import org.apache.iotdb.commons.schema.SchemaConstant; import org.apache.iotdb.commons.schema.column.ColumnHeader; import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; +import org.apache.iotdb.commons.schema.table.Audit; import org.apache.iotdb.commons.schema.template.Template; import org.apache.iotdb.commons.schema.view.LogicalViewSchema; import org.apache.iotdb.commons.schema.view.viewExpression.ViewExpression; @@ -71,6 +74,7 @@ import org.apache.iotdb.db.queryengine.plan.expression.binary.CompareBinaryExpre 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.multi.FunctionExpression; +import org.apache.iotdb.db.queryengine.plan.expression.visitor.CompleteMeasurementSchemaVisitor; import org.apache.iotdb.db.queryengine.plan.expression.visitor.ExistUnknownTypeInExpression; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.MeasurementGroup; import org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.DeviceViewIntoPathDescriptor; @@ -142,6 +146,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.sys.ExplainStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.ShowDiskUsageStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.ShowQueriesStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.ShowVersionStatement; +import org.apache.iotdb.db.schemaengine.schemaregion.view.visitor.TransformToExpressionVisitor; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -2931,7 +2936,9 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> Analysis analysis, MPPQueryContext context, PathPatternTree authorityScope, - boolean canSeeAuditDB) + boolean canSeeAuditDB, + boolean canSeeSystemDB, + boolean includeLogicalView) throws IllegalPathException { analyzeGlobalTimeConditionInShowMetaData(timeCondition, analysis); context.generateGlobalTimeFilter(analysis); @@ -2944,7 +2951,15 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analysis.setFinishQueryAfterAnalyze(true); return false; } - removeLogicViewMeasurement(schemaTree); + List<DeviceSchemaInfo> deviceSchemaInfoList; + if (includeLogicalView) { + deviceSchemaInfoList = schemaTree.getMatchedDevices(ALL_MATCH_PATTERN); + updateSchemaTreeByViews(analysis, schemaTree, context, canSeeAuditDB); + } else { + removeLogicViewMeasurement(schemaTree); + deviceSchemaInfoList = schemaTree.getMatchedDevices(ALL_MATCH_PATTERN); + } + Map<PartialPath, Map<PartialPath, List<TimeseriesContext>>> deviceToTimeseriesContext = new HashMap<>(); /** @@ -2952,38 +2967,60 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> * as a normal node, not a device+templateId. This means that all nodes are what we need.). We * can use ALL_MATCH_PATTERN to get result. */ - List<DeviceSchemaInfo> deviceSchemaInfoList = schemaTree.getMatchedDevices(ALL_MATCH_PATTERN); Set<IDeviceID> deviceSet = new HashSet<>(); for (DeviceSchemaInfo deviceSchemaInfo : deviceSchemaInfoList) { boolean isAligned = deviceSchemaInfo.isAligned(); PartialPath devicePath = deviceSchemaInfo.getDevicePath(); - deviceSet.add(devicePath.getIDeviceIDAsFullDevice()); + if (shouldSkipInternalDatabaseForActiveCount( + devicePath, schemaTree, canSeeAuditDB, canSeeSystemDB)) { + continue; + } if (isAligned) { List<String> measurementList = new ArrayList<>(); List<IMeasurementSchema> schemaList = new ArrayList<>(); List<TimeseriesContext> timeseriesContextList = new ArrayList<>(); for (IMeasurementSchemaInfo measurementSchemaInfo : deviceSchemaInfo.getMeasurementSchemaInfoList()) { + if (includeLogicalView && measurementSchemaInfo.isLogicalView()) { + addLogicalViewSourcesForActiveCount( + devicePath, + measurementSchemaInfo, + schemaTree, + deviceToTimeseriesContext, + deviceSet); + continue; + } schemaList.add(measurementSchemaInfo.getSchema()); measurementList.add(measurementSchemaInfo.getName()); timeseriesContextList.add(new TimeseriesContext(measurementSchemaInfo)); } - AlignedPath alignedPath = - new AlignedPath(devicePath.getNodes(), measurementList, schemaList); - deviceToTimeseriesContext - .computeIfAbsent(devicePath, k -> new HashMap<>()) - .put(alignedPath, timeseriesContextList); + if (!measurementList.isEmpty()) { + deviceSet.add(devicePath.getIDeviceIDAsFullDevice()); + AlignedPath alignedPath = + new AlignedPath(devicePath.getNodes(), measurementList, schemaList); + deviceToTimeseriesContext + .computeIfAbsent(devicePath, k -> new HashMap<>()) + .put(alignedPath, timeseriesContextList); + } } else { for (IMeasurementSchemaInfo measurementSchemaInfo : deviceSchemaInfo.getMeasurementSchemaInfoList()) { - MeasurementPath measurementPath = - new MeasurementPath( - devicePath.concatNode(measurementSchemaInfo.getName()).getNodes()); - deviceToTimeseriesContext - .computeIfAbsent(devicePath, k -> new HashMap<>()) - .put( - measurementPath, - Collections.singletonList(new TimeseriesContext(measurementSchemaInfo))); + if (includeLogicalView && measurementSchemaInfo.isLogicalView()) { + addLogicalViewSourcesForActiveCount( + devicePath, + measurementSchemaInfo, + schemaTree, + deviceToTimeseriesContext, + deviceSet); + } else { + addPhysicalTimeseriesForActiveCount( + devicePath, + measurementSchemaInfo, + false, + new TimeseriesContext(measurementSchemaInfo), + deviceToTimeseriesContext, + deviceSet); + } } } } @@ -2995,6 +3032,116 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> return true; } + private boolean shouldSkipInternalDatabaseForActiveCount( + PartialPath devicePath, + ISchemaTree schemaTree, + boolean canSeeAuditDB, + boolean canSeeSystemDB) { + String database = schemaTree.getBelongedDatabase(devicePath); + if (SchemaConstant.SYSTEM_DATABASE.equals(database)) { + return !canSeeSystemDB; + } + if (SchemaConstant.AUDIT_DATABASE.equals(database) + || Audit.TABLE_MODEL_AUDIT_DATABASE.equals(database)) { + return !canSeeAuditDB; + } + return false; + } + + private void addLogicalViewSourcesForActiveCount( + PartialPath viewDevicePath, + IMeasurementSchemaInfo viewSchemaInfo, + ISchemaTree schemaTree, + Map<PartialPath, Map<PartialPath, List<TimeseriesContext>>> deviceToTimeseriesContext, + Set<IDeviceID> deviceSet) { + LogicalViewSchema logicalViewSchema = viewSchemaInfo.getSchemaAsLogicalViewSchema(); + if (logicalViewSchema == null) { + return; + } + + String viewPath = viewDevicePath.concatNode(viewSchemaInfo.getName()).getFullPath(); + String viewDataType = getLogicalViewDataType(logicalViewSchema, schemaTree); + String viewDatabase = schemaTree.getBelongedDatabase(viewDevicePath); + for (PartialPath sourcePath : getSourcePaths(logicalViewSchema.getExpression())) { + if (sourcePath.getNodeLength() <= 1) { + continue; + } + PartialPath sourceDevicePath = + new PartialPath(Arrays.copyOf(sourcePath.getNodes(), sourcePath.getNodeLength() - 1)); + DeviceSchemaInfo sourceDeviceSchemaInfo = + schemaTree.searchDeviceSchemaInfo( + sourceDevicePath, Collections.singletonList(sourcePath.getMeasurement())); + if (sourceDeviceSchemaInfo == null + || sourceDeviceSchemaInfo.getMeasurementSchemaInfoList().isEmpty()) { + continue; + } + + IMeasurementSchemaInfo sourceSchemaInfo = + sourceDeviceSchemaInfo.getMeasurementSchemaInfoList().get(0); + if (sourceSchemaInfo == null || sourceSchemaInfo.isLogicalView()) { + continue; + } + + Map<String, TimeseriesContext> activeLogicalViewContextMap = + Collections.singletonMap( + viewPath, + new TimeseriesContext( + viewSchemaInfo, viewDataType, viewDatabase, 1, Collections.emptyMap())); + addPhysicalTimeseriesForActiveCount( + sourceDevicePath, + sourceSchemaInfo, + sourceDeviceSchemaInfo.isAligned(), + new TimeseriesContext(sourceSchemaInfo, 0, activeLogicalViewContextMap), + deviceToTimeseriesContext, + deviceSet); + } + } + + private String getLogicalViewDataType( + LogicalViewSchema logicalViewSchema, ISchemaTree schemaTree) { + if (logicalViewSchema.getType() != TSDataType.UNKNOWN) { + return logicalViewSchema.getType().toString(); + } + try { + Expression expression = + new TransformToExpressionVisitor().process(logicalViewSchema.getExpression(), null); + expression = new CompleteMeasurementSchemaVisitor().process(expression, schemaTree); + Map<NodeRef<Expression>, TSDataType> expressionTypes = new HashMap<>(); + analyzeExpression(expressionTypes, expression); + TSDataType dataType = expressionTypes.get(NodeRef.of(expression)); + return dataType == null ? TSDataType.UNKNOWN.toString() : dataType.toString(); + } catch (Exception e) { + return TSDataType.UNKNOWN.toString(); + } + } + + private void addPhysicalTimeseriesForActiveCount( + PartialPath devicePath, + IMeasurementSchemaInfo measurementSchemaInfo, + boolean isAligned, + TimeseriesContext timeseriesContext, + Map<PartialPath, Map<PartialPath, List<TimeseriesContext>>> deviceToTimeseriesContext, + Set<IDeviceID> deviceSet) { + deviceSet.add(devicePath.getIDeviceIDAsFullDevice()); + PartialPath timeseriesPath = + isAligned + ? new AlignedPath( + devicePath.getNodes(), + Collections.singletonList(measurementSchemaInfo.getName()), + Collections.singletonList(measurementSchemaInfo.getSchema())) + : new MeasurementPath( + devicePath.concatNode(measurementSchemaInfo.getName()).getNodes()); + Map<PartialPath, List<TimeseriesContext>> timeseriesContextMap = + deviceToTimeseriesContext.computeIfAbsent(devicePath, k -> new HashMap<>()); + List<TimeseriesContext> existingContextList = timeseriesContextMap.get(timeseriesPath); + if (existingContextList == null) { + timeseriesContextMap.put( + timeseriesPath, new ArrayList<>(Collections.singletonList(timeseriesContext))); + } else { + existingContextList.set(0, existingContextList.get(0).mergeActiveCount(timeseriesContext)); + } + } + @Override public Analysis visitShowTimeSeries( ShowTimeSeriesStatement showTimeSeriesStatement, MPPQueryContext context) { @@ -3020,7 +3167,9 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analysis, context, showTimeSeriesStatement.getAuthorityScope(), - showTimeSeriesStatement.isCanSeeAuditDB()); + showTimeSeriesStatement.isCanSeeAuditDB(), + true, + true); if (!hasSchema) { analysis.setRespDatasetHeader(DatasetHeaderFactory.getShowTimeSeriesHeader()); return analysis; @@ -3271,7 +3420,9 @@ public class AnalyzeVisitor extends StatementVisitor<Analysis, MPPQueryContext> analysis, context, countTimeSeriesStatement.getAuthorityScope(), - countTimeSeriesStatement.isCanSeeAuditDB()); + countTimeSeriesStatement.isCanSeeAuditDB(), + countTimeSeriesStatement.isCanSeeSystemDB(), + true); if (!hasSchema) { analysis.setRespDatasetHeader(DatasetHeaderFactory.getCountTimeSeriesHeader()); return analysis; 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 fff2aa07b49..26d361c1f21 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 @@ -1176,7 +1176,9 @@ public class LogicalPlanBuilder { boolean prefixPath, SchemaFilter schemaFilter, Map<Integer, Template> templateMap, - PathPatternTree scope) { + PathPatternTree scope, + boolean includeSystemDatabase, + boolean includeAuditDatabase) { this.root = new TimeSeriesCountNode( context.getQueryId().genPlanNodeId(), @@ -1184,7 +1186,9 @@ public class LogicalPlanBuilder { prefixPath, schemaFilter, templateMap, - scope); + scope, + includeSystemDatabase, + includeAuditDatabase); return this; } @@ -1194,7 +1198,9 @@ public class LogicalPlanBuilder { int level, SchemaFilter schemaFilter, Map<Integer, Template> templateMap, - PathPatternTree scope) { + PathPatternTree scope, + boolean includeSystemDatabase, + boolean includeAuditDatabase) { this.root = new LevelTimeSeriesCountNode( context.getQueryId().genPlanNodeId(), @@ -1203,7 +1209,9 @@ public class LogicalPlanBuilder { level, schemaFilter, templateMap, - scope); + scope, + includeSystemDatabase, + includeAuditDatabase); 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 a9b0b9d83f4..2c9304ce0b9 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 @@ -733,7 +733,9 @@ public class LogicalPlanVisitor extends StatementVisitor<PlanNode, MPPQueryConte countTimeSeriesStatement.isPrefixPath(), countTimeSeriesStatement.getSchemaFilter(), analysis.getRelatedTemplateInfo(), - countTimeSeriesStatement.getAuthorityScope()) + countTimeSeriesStatement.getAuthorityScope(), + countTimeSeriesStatement.isCanSeeSystemDB(), + countTimeSeriesStatement.isCanSeeAuditDB()) .planCountMerge() .getRoot(); } @@ -749,7 +751,9 @@ public class LogicalPlanVisitor extends StatementVisitor<PlanNode, MPPQueryConte countLevelTimeSeriesStatement.getLevel(), countLevelTimeSeriesStatement.getSchemaFilter(), analysis.getRelatedTemplateInfo(), - countLevelTimeSeriesStatement.getAuthorityScope()) + countLevelTimeSeriesStatement.getAuthorityScope(), + countLevelTimeSeriesStatement.isCanSeeSystemDB(), + countLevelTimeSeriesStatement.isCanSeeAuditDB()) .planCountMerge() .getRoot(); } 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 9f1aa7fc291..63cb0d861ad 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 @@ -984,7 +984,9 @@ public class OperatorTreeGenerator implements PlanVisitor<Operator, LocalExecuti node.isPrefixPath(), node.getSchemaFilter(), node.getTemplateMap(), - node.getScope())); + node.getScope(), + node.isIncludeSystemDatabase(), + node.isIncludeAuditDatabase())); } @Override @@ -1006,7 +1008,9 @@ public class OperatorTreeGenerator implements PlanVisitor<Operator, LocalExecuti node.isPrefixPath(), node.getSchemaFilter(), node.getTemplateMap(), - node.getScope())); + node.getScope(), + node.isIncludeSystemDatabase(), + node.isIncludeAuditDatabase())); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java index 6582f0a4a68..d326a6de30a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/SourceRewriter.java @@ -858,7 +858,10 @@ public class SourceRewriter extends BaseSourceRewriter<DistributionPlanContext> return planNodeList; } - boolean outputCountInScanNode = node.isOutputCount() && !context.isOneSeriesInMultiRegion(); + boolean outputCountInScanNode = + node.isOutputCount() + && !context.isOneSeriesInMultiRegion() + && !hasActiveLogicalViewContext(node); ActiveRegionScanMergeNode regionMergeNode = new ActiveRegionScanMergeNode( context.queryContext.getQueryId().genPlanNodeId(), @@ -872,6 +875,11 @@ public class SourceRewriter extends BaseSourceRewriter<DistributionPlanContext> return Collections.singletonList(regionMergeNode); } + private boolean hasActiveLogicalViewContext(RegionScanNode node) { + return node instanceof TimeseriesRegionScanNode + && ((TimeseriesRegionScanNode) node).hasActiveLogicalViewContext(); + } + @Override public List<PlanNode> visitDeviceRegionScan( DeviceRegionScanNode node, DistributionPlanContext context) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/LevelTimeSeriesCountNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/LevelTimeSeriesCountNode.java index 336ae40f85d..902a2dd69db 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/LevelTimeSeriesCountNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/LevelTimeSeriesCountNode.java @@ -48,6 +48,8 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { private final int level; private final SchemaFilter schemaFilter; private final Map<Integer, Template> templateMap; + private final boolean includeSystemDatabase; + private final boolean includeAuditDatabase; public LevelTimeSeriesCountNode( PlanNodeId id, @@ -56,11 +58,15 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { int level, SchemaFilter schemaFilter, @NotNull Map<Integer, Template> templateMap, - @NotNull PathPatternTree scope) { + @NotNull PathPatternTree scope, + boolean includeSystemDatabase, + boolean includeAuditDatabase) { super(id, partialPath, isPrefixPath, scope); this.level = level; this.schemaFilter = schemaFilter; this.templateMap = templateMap; + this.includeSystemDatabase = includeSystemDatabase; + this.includeAuditDatabase = includeAuditDatabase; } public SchemaFilter getSchemaFilter() { @@ -75,6 +81,14 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { return templateMap; } + public boolean isIncludeSystemDatabase() { + return includeSystemDatabase; + } + + public boolean isIncludeAuditDatabase() { + return includeAuditDatabase; + } + @Override public PlanNodeType getType() { return PlanNodeType.LEVEL_TIME_SERIES_COUNT; @@ -83,7 +97,15 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { @Override public PlanNode clone() { return new LevelTimeSeriesCountNode( - getPlanNodeId(), path, isPrefixPath, level, schemaFilter, templateMap, scope); + getPlanNodeId(), + path, + isPrefixPath, + level, + schemaFilter, + templateMap, + scope, + includeSystemDatabase, + includeAuditDatabase); } @Override @@ -101,6 +123,8 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { ReadWriteIOUtils.write(isPrefixPath, byteBuffer); ReadWriteIOUtils.write(level, byteBuffer); SchemaFilter.serialize(schemaFilter, byteBuffer); + ReadWriteIOUtils.write(includeSystemDatabase, byteBuffer); + ReadWriteIOUtils.write(includeAuditDatabase, byteBuffer); ReadWriteIOUtils.write(templateMap.size(), byteBuffer); for (Template template : templateMap.values()) { template.serialize(byteBuffer); @@ -115,6 +139,8 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { ReadWriteIOUtils.write(isPrefixPath, stream); ReadWriteIOUtils.write(level, stream); SchemaFilter.serialize(schemaFilter, stream); + ReadWriteIOUtils.write(includeSystemDatabase, stream); + ReadWriteIOUtils.write(includeAuditDatabase, stream); ReadWriteIOUtils.write(templateMap.size(), stream); for (Template template : templateMap.values()) { template.serialize(stream); @@ -134,6 +160,8 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { boolean isPrefixPath = ReadWriteIOUtils.readBool(buffer); int level = ReadWriteIOUtils.readInt(buffer); SchemaFilter schemaFilter = SchemaFilter.deserialize(buffer); + boolean includeSystemDatabase = ReadWriteIOUtils.readBool(buffer); + boolean includeAuditDatabase = ReadWriteIOUtils.readBool(buffer); int templateNum = ReadWriteIOUtils.readInt(buffer); Map<Integer, Template> templateMap = new HashMap<>(); Template template; @@ -144,7 +172,15 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { } PlanNodeId planNodeId = PlanNodeId.deserialize(buffer); return new LevelTimeSeriesCountNode( - planNodeId, path, isPrefixPath, level, schemaFilter, templateMap, scope); + planNodeId, + path, + isPrefixPath, + level, + schemaFilter, + templateMap, + scope, + includeSystemDatabase, + includeAuditDatabase); } @Override @@ -159,12 +195,14 @@ public class LevelTimeSeriesCountNode extends SchemaQueryScanNode { return false; } LevelTimeSeriesCountNode that = (LevelTimeSeriesCountNode) o; - return level == that.level; + return level == that.level + && includeSystemDatabase == that.includeSystemDatabase + && includeAuditDatabase == that.includeAuditDatabase; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), level); + return Objects.hash(super.hashCode(), level, includeSystemDatabase, includeAuditDatabase); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesCountNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesCountNode.java index 6326a38a703..8bcc9e70f5d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesCountNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/read/TimeSeriesCountNode.java @@ -51,16 +51,24 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { private final Map<Integer, Template> templateMap; + private final boolean includeSystemDatabase; + + private final boolean includeAuditDatabase; + public TimeSeriesCountNode( PlanNodeId id, PartialPath partialPath, boolean isPrefixPath, SchemaFilter schemaFilter, @NotNull Map<Integer, Template> templateMap, - @NotNull PathPatternTree scope) { + @NotNull PathPatternTree scope, + boolean includeSystemDatabase, + boolean includeAuditDatabase) { super(id, partialPath, isPrefixPath, scope); this.schemaFilter = schemaFilter; this.templateMap = templateMap; + this.includeSystemDatabase = includeSystemDatabase; + this.includeAuditDatabase = includeAuditDatabase; } public SchemaFilter getSchemaFilter() { @@ -71,6 +79,14 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { return templateMap; } + public boolean isIncludeSystemDatabase() { + return includeSystemDatabase; + } + + public boolean isIncludeAuditDatabase() { + return includeAuditDatabase; + } + @Override public PlanNodeType getType() { return PlanNodeType.TIME_SERIES_COUNT; @@ -79,7 +95,14 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { @Override public PlanNode clone() { return new TimeSeriesCountNode( - getPlanNodeId(), path, isPrefixPath, schemaFilter, templateMap, scope); + getPlanNodeId(), + path, + isPrefixPath, + schemaFilter, + templateMap, + scope, + includeSystemDatabase, + includeAuditDatabase); } @Override @@ -96,6 +119,8 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { scope.serialize(byteBuffer); ReadWriteIOUtils.write(isPrefixPath, byteBuffer); SchemaFilter.serialize(schemaFilter, byteBuffer); + ReadWriteIOUtils.write(includeSystemDatabase, byteBuffer); + ReadWriteIOUtils.write(includeAuditDatabase, byteBuffer); ReadWriteIOUtils.write(templateMap.size(), byteBuffer); for (Template template : templateMap.values()) { template.serialize(byteBuffer); @@ -109,6 +134,8 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { scope.serialize(stream); ReadWriteIOUtils.write(isPrefixPath, stream); SchemaFilter.serialize(schemaFilter, stream); + ReadWriteIOUtils.write(includeSystemDatabase, stream); + ReadWriteIOUtils.write(includeAuditDatabase, stream); ReadWriteIOUtils.write(templateMap.size(), stream); for (Template template : templateMap.values()) { template.serialize(stream); @@ -127,6 +154,8 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { PathPatternTree scope = PathPatternTree.deserialize(buffer); boolean isPrefixPath = ReadWriteIOUtils.readBool(buffer); SchemaFilter schemaFilter = SchemaFilter.deserialize(buffer); + boolean includeSystemDatabase = ReadWriteIOUtils.readBool(buffer); + boolean includeAuditDatabase = ReadWriteIOUtils.readBool(buffer); int templateNum = ReadWriteIOUtils.readInt(buffer); Map<Integer, Template> templateMap = new HashMap<>(); @@ -139,7 +168,14 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { PlanNodeId planNodeId = PlanNodeId.deserialize(buffer); return new TimeSeriesCountNode( - planNodeId, path, isPrefixPath, schemaFilter, templateMap, scope); + planNodeId, + path, + isPrefixPath, + schemaFilter, + templateMap, + scope, + includeSystemDatabase, + includeAuditDatabase); } @Override @@ -156,11 +192,14 @@ public class TimeSeriesCountNode extends SchemaQueryScanNode { if (!super.equals(o)) return false; TimeSeriesCountNode that = (TimeSeriesCountNode) o; return Objects.equals(schemaFilter, that.schemaFilter) - && Objects.equals(templateMap, that.templateMap); + && Objects.equals(templateMap, that.templateMap) + && includeSystemDatabase == that.includeSystemDatabase + && includeAuditDatabase == that.includeAuditDatabase; } @Override public int hashCode() { - return Objects.hash(super.hashCode(), schemaFilter, templateMap); + return Objects.hash( + super.hashCode(), schemaFilter, templateMap, includeSystemDatabase, includeAuditDatabase); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java index b960aa5b507..24bcf288403 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/source/TimeseriesRegionScanNode.java @@ -86,6 +86,13 @@ public class TimeseriesRegionScanNode extends RegionScanNode { return deviceToTimeseriesSchemaInfo; } + public boolean hasActiveLogicalViewContext() { + return deviceToTimeseriesSchemaInfo.values().stream() + .flatMap(timeseriesContextMap -> timeseriesContextMap.values().stream()) + .flatMap(List::stream) + .anyMatch(context -> !context.getActiveLogicalViewContextMap().isEmpty()); + } + @Override public List<PlanNode> getChildren() { return ImmutableList.of(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java index 68658529e71..fd9757c5f89 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java @@ -30,6 +30,7 @@ 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.PathPatternTreeUtils; +import org.apache.iotdb.commons.schema.SchemaConstant; import org.apache.iotdb.commons.schema.table.Audit; import org.apache.iotdb.commons.utils.StatusUtils; import org.apache.iotdb.db.audit.DNAuditLogger; @@ -54,6 +55,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountDatabaseStat import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountDevicesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountLevelTimeSeriesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountNodesStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountTimeSeriesStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountTimeSlotListStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateAlignedTimeSeriesStatement; @@ -1393,13 +1395,19 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces .setAuditLogOperation(AuditLogOperation.QUERY) .setPrivilegeType(PrivilegeType.READ_SCHEMA); if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) { + statement.setCanSeeSystemDB(true); statement.setCanSeeAuditDB(true); AUDIT_LOGGER.recordObjectAuthenticationAuditLog( context.setResult(true), () -> statement.getPaths().stream().distinct().collect(Collectors.toList()).toString()); return SUCCEED; } + TSStatus internalDatabaseStatus = checkExplicitInternalDatabaseCount(statement, context); + if (internalDatabaseStatus != null) { + return internalDatabaseStatus; + } setCanSeeAuditDB(statement, context); + setCanSeeSystemDB(statement, context); if (statement.hasTimeCondition()) { try { statement.setAuthorityScope( @@ -1408,6 +1416,7 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces context.getUsername(), PrivilegeType.READ_SCHEMA), AuthorityChecker.getAuthorizedPathTree( context.getUsername(), PrivilegeType.READ_DATA))); + appendInternalDatabaseAuthorityScope(statement); } catch (AuthException e) { AUDIT_LOGGER.recordObjectAuthenticationAuditLog( context.setResult(false), @@ -1419,14 +1428,22 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces () -> statement.getPaths().stream().distinct().collect(Collectors.toList()).toString()); return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } else { - return visitAuthorityInformation(statement, context); + TSStatus status = visitAuthorityInformation(statement, context); + if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + appendInternalDatabaseAuthorityScope(statement); + } + return status; } } @Override public TSStatus visitCountLevelTimeSeries( CountLevelTimeSeriesStatement countStatement, TreeAccessCheckContext context) { + context + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.READ_SCHEMA); if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) { + countStatement.setCanSeeSystemDB(true); countStatement.setCanSeeAuditDB(true); AUDIT_LOGGER.recordObjectAuthenticationAuditLog( context.setResult(true), @@ -1437,8 +1454,86 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces .toString()); return SUCCEED; } + TSStatus internalDatabaseStatus = checkExplicitInternalDatabaseCount(countStatement, context); + if (internalDatabaseStatus != null) { + return internalDatabaseStatus; + } setCanSeeAuditDB(countStatement, context); - return visitAuthorityInformation(countStatement, context); + setCanSeeSystemDB(countStatement, context); + TSStatus status = visitAuthorityInformation(countStatement, context); + if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + appendInternalDatabaseAuthorityScope(countStatement); + } + return status; + } + + private void setCanSeeSystemDB(CountStatement statement, IAuditEntity auditEntity) { + statement.setCanSeeSystemDB( + checkHasGlobalAuth( + auditEntity, PrivilegeType.SYSTEM, () -> SchemaConstant.SYSTEM_DATABASE)); + } + + private void appendInternalDatabaseAuthorityScope(CountStatement statement) { + PathPatternTree authorityScope = statement.getAuthorityScope(); + if (SchemaConstant.ALL_MATCH_SCOPE.equals(authorityScope)) { + return; + } + if (statement.isCanSeeSystemDB()) { + authorityScope.appendPathPattern( + createInternalDatabasePathPattern(SchemaConstant.SYSTEM_DATABASE), true); + } + if (statement.isCanSeeAuditDB()) { + authorityScope.appendPathPattern(Audit.TREE_MODEL_AUDIT_DATABASE_PATH_PATTERN, true); + } + authorityScope.constructTree(); + } + + private PartialPath createInternalDatabasePathPattern(String internalDatabase) { + String[] databaseNodes = internalDatabase.split("\\."); + String[] pathPatternNodes = Arrays.copyOf(databaseNodes, databaseNodes.length + 1); + pathPatternNodes[databaseNodes.length] = IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD; + return new PartialPath(pathPatternNodes); + } + + private TSStatus checkExplicitInternalDatabaseCount( + CountStatement statement, TreeAccessCheckContext context) { + String internalDatabase = getExplicitInternalDatabase(statement.getPathPattern()); + if (internalDatabase == null) { + return null; + } + + PrivilegeType requiredPrivilege = + SchemaConstant.SYSTEM_DATABASE.equals(internalDatabase) + ? PrivilegeType.SYSTEM + : PrivilegeType.AUDIT; + TSStatus status = checkGlobalAuth(context, requiredPrivilege, () -> internalDatabase); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + return status; + } + statement.setCanSeeSystemDB(SchemaConstant.SYSTEM_DATABASE.equals(internalDatabase)); + statement.setCanSeeAuditDB(SchemaConstant.AUDIT_DATABASE.equals(internalDatabase)); + statement.setAuthorityScope(createAuthorityScope(statement.getPathPattern())); + return SUCCEED; + } + + private String getExplicitInternalDatabase(PartialPath pathPattern) { + String[] nodes = pathPattern.getNodes(); + if (nodes.length < 2 || !SchemaConstant.ROOT.equals(nodes[0])) { + return null; + } + String database = SchemaConstant.ROOT + "." + nodes[1]; + if (SchemaConstant.SYSTEM_DATABASE.equals(database) + || SchemaConstant.AUDIT_DATABASE.equals(database)) { + return database; + } + return null; + } + + private PathPatternTree createAuthorityScope(PartialPath pathPattern) { + PathPatternTree authorityScope = new PathPatternTree(); + authorityScope.appendPathPattern(pathPattern); + authorityScope.constructTree(); + return authorityScope; } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountStatement.java index 529a8660dfb..a74390b7bed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountStatement.java @@ -34,6 +34,7 @@ import java.util.List; */ public class CountStatement extends ShowStatement { protected PartialPath pathPattern; + private boolean canSeeSystemDB = true; public CountStatement(PartialPath pathPattern) { this.pathPattern = pathPattern; @@ -52,4 +53,12 @@ public class CountStatement extends ShowStatement { public List<PartialPath> getPaths() { return Collections.singletonList(pathPattern); } + + public boolean isCanSeeSystemDB() { + return canSeeSystemDB; + } + + public void setCanSeeSystemDB(boolean canSeeSystemDB) { + this.canSeeSystemDB = canSeeSystemDB; + } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java index af8746fae3e..dd1826982c9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java @@ -21,9 +21,12 @@ package org.apache.iotdb.db.auth; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckVisitor; import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountLevelTimeSeriesStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.CountTimeSeriesStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; import org.apache.iotdb.rpc.TSStatusCode; @@ -33,6 +36,8 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mockito; +import java.util.Collections; + public class TreeAccessTest { @Before @@ -81,4 +86,132 @@ public class TreeAccessTest { .visitAuthor(authorStatement, new TreeAccessCheckContext(10000L, "user1", "")) .getCode()); } + + @Test + public void testCountTimeSeriesInternalDatabasePermission() throws Exception { + User user = new User("user1", "password"); + AuthorityChecker.getAuthorityFetcher().getAuthorCache().putUserCache(user.getName(), user); + TreeAccessCheckVisitor treeAccessCheckVisitor = new TreeAccessCheckVisitor(); + + CountTimeSeriesStatement systemStatement = + new CountTimeSeriesStatement(new PartialPath("root.__system.**")); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(systemStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + + user.grantSysPrivilege(PrivilegeType.SYSTEM, false); + systemStatement = new CountTimeSeriesStatement(new PartialPath("root.__system.**")); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(systemStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + Assert.assertEquals( + Collections.singletonList(new PartialPath("root.__system.**")), + systemStatement.getAuthorityScope().getAllPathPatterns()); + Assert.assertTrue(systemStatement.isCanSeeSystemDB()); + Assert.assertFalse(systemStatement.isCanSeeAuditDB()); + + CountLevelTimeSeriesStatement systemLevelStatement = + new CountLevelTimeSeriesStatement(new PartialPath("root.__system.**"), 1); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountLevelTimeSeries( + systemLevelStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + Assert.assertEquals( + Collections.singletonList(new PartialPath("root.__system.**")), + systemLevelStatement.getAuthorityScope().getAllPathPatterns()); + Assert.assertTrue(systemLevelStatement.isCanSeeSystemDB()); + Assert.assertFalse(systemLevelStatement.isCanSeeAuditDB()); + + CountTimeSeriesStatement auditStatement = + new CountTimeSeriesStatement(new PartialPath("root.__audit.**")); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(auditStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + + user.grantSysPrivilege(PrivilegeType.AUDIT, false); + auditStatement = new CountTimeSeriesStatement(new PartialPath("root.__audit.**")); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(auditStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + Assert.assertTrue(auditStatement.isCanSeeAuditDB()); + Assert.assertFalse(auditStatement.isCanSeeSystemDB()); + Assert.assertEquals( + Collections.singletonList(new PartialPath("root.__audit.**")), + auditStatement.getAuthorityScope().getAllPathPatterns()); + + CountLevelTimeSeriesStatement auditLevelStatement = + new CountLevelTimeSeriesStatement(new PartialPath("root.__audit.**"), 1); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountLevelTimeSeries( + auditLevelStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + Assert.assertTrue(auditLevelStatement.isCanSeeAuditDB()); + Assert.assertFalse(auditLevelStatement.isCanSeeSystemDB()); + Assert.assertEquals( + Collections.singletonList(new PartialPath("root.__audit.**")), + auditLevelStatement.getAuthorityScope().getAllPathPatterns()); + } + + @Test + public void testCountTimeSeriesImplicitInternalDatabasePermission() throws Exception { + User user = new User("user2", "password"); + AuthorityChecker.getAuthorityFetcher().getAuthorCache().putUserCache(user.getName(), user); + TreeAccessCheckVisitor treeAccessCheckVisitor = new TreeAccessCheckVisitor(); + + CountTimeSeriesStatement statement = new CountTimeSeriesStatement(new PartialPath("root.**")); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(statement, new TreeAccessCheckContext(10000L, "user2", "")) + .getCode()); + Assert.assertFalse(statement.isCanSeeSystemDB()); + Assert.assertFalse(statement.isCanSeeAuditDB()); + + user.grantSysPrivilege(PrivilegeType.SYSTEM, false); + statement = new CountTimeSeriesStatement(new PartialPath("root.**")); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(statement, new TreeAccessCheckContext(10000L, "user2", "")) + .getCode()); + Assert.assertTrue(statement.isCanSeeSystemDB()); + Assert.assertFalse(statement.isCanSeeAuditDB()); + Assert.assertTrue( + statement + .getAuthorityScope() + .getAllPathPatterns() + .contains(new PartialPath("root.__system.**"))); + + user.grantSysPrivilege(PrivilegeType.AUDIT, false); + statement = new CountTimeSeriesStatement(new PartialPath("root.**")); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitCountTimeSeries(statement, new TreeAccessCheckContext(10000L, "user2", "")) + .getCode()); + Assert.assertTrue(statement.isCanSeeSystemDB()); + Assert.assertTrue(statement.isCanSeeAuditDB()); + Assert.assertTrue( + statement + .getAuthorityScope() + .getAllPathPatterns() + .contains(new PartialPath("root.__system.**"))); + Assert.assertTrue( + statement + .getAuthorityScope() + .getAllPathPatterns() + .contains(new PartialPath("root.__audit.**"))); + } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSourceTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSourceTest.java new file mode 100644 index 00000000000..307e69a179e --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TimeSeriesSchemaSourceTest.java @@ -0,0 +1,183 @@ +/* + * 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.queryengine.execution.operator.schema.source; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.schema.SchemaConstant; +import org.apache.iotdb.commons.schema.table.Audit; +import org.apache.iotdb.db.schemaengine.rescon.ISchemaRegionStatistics; +import org.apache.iotdb.db.schemaengine.schemaregion.ISchemaRegion; +import org.apache.iotdb.db.schemaengine.schemaregion.read.resp.info.ITimeSeriesSchemaInfo; + +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Collections; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class TimeSeriesSchemaSourceTest { + + @Test + public void testCountSourceSkipsUnauthorizedInternalDatabases() throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> countSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.**"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + false, + false); + + assertTrue( + countSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + assertTrue(countSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + assertTrue( + countSource.shouldSkipSchemaRegion(mockSchemaRegion(Audit.TABLE_MODEL_AUDIT_DATABASE))); + assertFalse(countSource.shouldSkipSchemaRegion(mockSchemaRegion("root.sg"))); + } + + @Test + public void testCountSourceKeepsAuthorizedInternalDatabases() throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> systemCountSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.**"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + true, + false); + assertFalse( + systemCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + assertTrue( + systemCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + + final ISchemaSource<ITimeSeriesSchemaInfo> auditCountSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.**"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + false, + true); + assertFalse( + auditCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + assertTrue( + auditCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + } + + @Test + public void testCountSourceSkipsUnauthorizedInternalDatabasesWithWildcardSecondNode() + throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> countSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.*.**"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + false, + false); + + assertTrue( + countSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + assertTrue(countSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + assertFalse(countSource.shouldSkipSchemaRegion(mockSchemaRegion("root.sg"))); + } + + @Test + public void testCountSourceKeepsExactInternalDatabaseQueriesWithPrivilege() throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> systemCountSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.__system"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + true, + false); + assertFalse( + systemCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + + final ISchemaSource<ITimeSeriesSchemaInfo> auditCountSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.__audit"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + false, + true); + assertFalse( + auditCountSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + } + + @Test + public void testShowSourceDoesNotSkipInternalDatabases() throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> showSource = + SchemaSourceFactory.getTimeSeriesSchemaScanSource( + new PartialPath("root.**"), + false, + 0, + 0, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + null); + + assertFalse( + showSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.SYSTEM_DATABASE))); + assertFalse(showSource.shouldSkipSchemaRegion(mockSchemaRegion(SchemaConstant.AUDIT_DATABASE))); + } + + @Test + public void testCountStatisticIncludesView() throws Exception { + final ISchemaSource<ITimeSeriesSchemaInfo> countSource = + SchemaSourceFactory.getTimeSeriesSchemaCountSource( + new PartialPath("root.sg.**"), + false, + null, + Collections.emptyMap(), + SchemaConstant.ALL_MATCH_SCOPE, + false, + false); + final ISchemaRegion schemaRegion = mockSchemaRegion("root.sg"); + final ISchemaRegionStatistics schemaRegionStatistics = + Mockito.mock(ISchemaRegionStatistics.class); + + Mockito.when(schemaRegion.getSchemaRegionStatistics()).thenReturn(schemaRegionStatistics); + Mockito.when(schemaRegionStatistics.getSeriesNumber(true)).thenReturn(5L); + + assertEquals(5L, countSource.getSchemaStatistic(schemaRegion)); + Mockito.verify(schemaRegionStatistics).getSeriesNumber(true); + Mockito.verify(schemaRegionStatistics, Mockito.never()).getSeriesNumber(false); + } + + private ISchemaRegion mockSchemaRegion(final String database) { + final ISchemaRegion schemaRegion = Mockito.mock(ISchemaRegion.class); + Mockito.when(schemaRegion.getDatabaseFullPath()).thenReturn(database); + return schemaRegion; + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/RegionScanPlanningTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/RegionScanPlanningTest.java index 205758304e0..892fc4ee3dd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/RegionScanPlanningTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/distribution/RegionScanPlanningTest.java @@ -26,6 +26,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; import org.apache.iotdb.db.queryengine.common.MPPQueryContext; import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.common.TimeseriesContext; import org.apache.iotdb.db.queryengine.plan.analyze.Analysis; import org.apache.iotdb.db.queryengine.plan.planner.plan.DistributedQueryPlan; import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; @@ -36,10 +37,15 @@ import org.apache.iotdb.db.queryengine.plan.planner.plan.node.source.TimeseriesR import org.junit.Test; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class RegionScanPlanningTest { @@ -145,4 +151,79 @@ public class RegionScanPlanningTest { assertEquals(devicePaths, targetDevicePaths); assertEquals(path, targetMeasurementPaths); } + + @Test + public void testCountTimeseriesWithLogicalViewUsesMergeBeforeCount() throws IllegalPathException { + QueryId queryId = new QueryId("test"); + MPPQueryContext context = + new MPPQueryContext("", queryId, null, new TEndPoint(), new TEndPoint()); + + Map<String, TimeseriesContext> logicalViewContextMap = + Collections.singletonMap( + "root.sg.view.v1", + new TimeseriesContext( + "INT32", + null, + null, + null, + null, + null, + null, + null, + 1, + true, + "root.sg", + Collections.emptyMap())); + Map<PartialPath, Map<PartialPath, List<TimeseriesContext>>> deviceToTimeseriesSchemaInfo = + new HashMap<>(); + deviceToTimeseriesSchemaInfo.put( + new PartialPath("root.sg.d22"), + Collections.singletonMap( + new MeasurementPath("root.sg.d22.s1"), + Collections.singletonList( + new TimeseriesContext( + "INT32", + null, + "PLAIN", + "LZ4", + null, + null, + null, + null, + 0, + logicalViewContextMap)))); + deviceToTimeseriesSchemaInfo.put( + new PartialPath("root.sg.d55555"), + Collections.singletonMap( + new MeasurementPath("root.sg.d55555.s1"), + Collections.singletonList( + new TimeseriesContext( + "INT32", + null, + "PLAIN", + "LZ4", + null, + null, + null, + null, + 0, + logicalViewContextMap)))); + + TimeseriesRegionScanNode regionScanNode = + new TimeseriesRegionScanNode( + queryId.genPlanNodeId(), deviceToTimeseriesSchemaInfo, true, null); + PlanNode rewrittenRoot = + new DistributionPlanner(Util.ANALYSIS, new LogicalQueryPlan(context, regionScanNode)) + .rewriteSource(); + + assertTrue(rewrittenRoot instanceof ActiveRegionScanMergeNode); + ActiveRegionScanMergeNode mergeNode = (ActiveRegionScanMergeNode) rewrittenRoot; + assertTrue(mergeNode.isOutputCount()); + assertTrue(mergeNode.isNeedMerge()); + assertEquals(2, mergeNode.getChildren().size()); + for (PlanNode child : mergeNode.getChildren()) { + assertTrue(child instanceof TimeseriesRegionScanNode); + assertFalse(((TimeseriesRegionScanNode) child).isOutputCount()); + } + } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/RegionScanLogicalPlannerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/RegionScanLogicalPlannerTest.java index dd3b8b6281e..6975d845c03 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/RegionScanLogicalPlannerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/logical/RegionScanLogicalPlannerTest.java @@ -276,4 +276,51 @@ public class RegionScanLogicalPlannerTest { buffer.flip(); Assert.assertEquals(timeseriesRegionScanNode, PlanNodeType.deserialize(buffer)); } + + @Test + public void serializeDeserializeLogicalViewContextTest() throws IllegalPathException { + Map<String, TimeseriesContext> logicalViewContextMap = + Collections.singletonMap( + "root.sg.view.v1", + new TimeseriesContext( + "INT32", + null, + null, + null, + null, + null, + null, + null, + 1, + true, + "root.sg", + new HashMap<>())); + Map<PartialPath, List<TimeseriesContext>> timeseriesSchemaInfoMap = new HashMap<>(); + timeseriesSchemaInfoMap.put( + new MeasurementPath("root.sg.d3.s1", TSDataType.INT32), + Collections.singletonList( + new TimeseriesContext( + "INT32", + null, + config.getDefaultInt32Encoding().toString(), + "LZ4", + null, + null, + null, + null, + 0, + logicalViewContextMap))); + Map<PartialPath, Map<PartialPath, List<TimeseriesContext>>> deviceToTimeseriesSchemaInfo = + new HashMap<>(); + deviceToTimeseriesSchemaInfo.put(new PartialPath("root.sg.d3"), timeseriesSchemaInfoMap); + + TimeseriesRegionScanNode timeseriesRegionScanNode = + new TimeseriesRegionScanNode( + new PlanNodeId("timeseries_test_id"), deviceToTimeseriesSchemaInfo, false, null); + + ByteBuffer buffer = ByteBuffer.allocate(10240); + timeseriesRegionScanNode.serialize(buffer); + buffer.flip(); + Assert.assertEquals(timeseriesRegionScanNode, PlanNodeType.deserialize(buffer)); + } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/metadata/read/SchemaCountNodeSerdeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/metadata/read/SchemaCountNodeSerdeTest.java index b5dca437289..6be0ad7fae5 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/metadata/read/SchemaCountNodeSerdeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/planner/node/metadata/read/SchemaCountNodeSerdeTest.java @@ -87,7 +87,9 @@ public class SchemaCountNodeSerdeTest { 10, null, Collections.emptyMap(), - SchemaConstant.ALL_MATCH_SCOPE); + SchemaConstant.ALL_MATCH_SCOPE, + true, + true); IdentitySinkNode sinkNode = new IdentitySinkNode( new PlanNodeId("sink"), @@ -122,7 +124,9 @@ public class SchemaCountNodeSerdeTest { true, null, Collections.emptyMap(), - SchemaConstant.ALL_MATCH_SCOPE); + SchemaConstant.ALL_MATCH_SCOPE, + true, + true); IdentitySinkNode sinkNode = new IdentitySinkNode( new PlanNodeId("sink"),
