This is an automated email from the ASF dual-hosted git repository.
CRZbulabula 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 0c25e53281f Implemented the count database in table model (#17705)
0c25e53281f is described below
commit 0c25e53281f54c360bb33264b7acbc06fcf32bdc
Author: Caideyipi <[email protected]>
AuthorDate: Tue May 26 14:25:59 2026 +0800
Implemented the count database in table model (#17705)
---
.../relational/it/schema/IoTDBDatabaseIT.java | 27 ++++++
.../iotdb/db/queryengine/plan/Coordinator.java | 2 +
.../execution/config/TableConfigTaskVisitor.java | 11 +++
.../config/executor/ClusterConfigTaskExecutor.java | 24 ++++-
.../config/executor/IConfigTaskExecutor.java | 8 +-
.../config/metadata/relational/CountDBTask.java | 82 ++++++++++++++++
.../config/metadata/relational/ShowDBTask.java | 22 ++---
.../plan/relational/sql/ast/AstVisitor.java | 4 +
.../plan/relational/sql/ast/CountDB.java | 77 +++++++++++++++
.../plan/relational/sql/parser/AstBuilder.java | 7 ++
.../relational/sql/util/DataNodeSqlFormatter.java | 9 +-
.../metadata/relational/CountDBTaskTest.java | 107 +++++++++++++++++++++
.../sql/parser/CountDBStatementTest.java | 65 +++++++++++++
.../db/relational/grammar/sql/RelationalSql.g4 | 5 +
14 files changed, 434 insertions(+), 16 deletions(-)
diff --git
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
index 46d27d997e9..4eab11ca3c7 100644
---
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
+++
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
@@ -816,6 +816,12 @@ public class IoTDBDatabaseIT {
try (final Connection connection =
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
final Statement statement = connection.createStatement()) {
+ TestUtils.assertResultSetEqual(
+ statement.executeQuery("count databases"), "count,",
Collections.singleton("3,"));
+ TestUtils.assertResultSetEqual(
+ statement.executeQuery("select count(*) from
information_schema.databases"),
+ "_col0,",
+ Collections.singleton("3,"));
statement.execute("drop database test");
}
@@ -826,6 +832,23 @@ public class IoTDBDatabaseIT {
}
}
+ @Test
+ public void testCountDatabases() throws SQLException {
+ try (final Connection connection =
+ EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+ final Statement statement = connection.createStatement()) {
+ statement.execute("create database db1");
+ statement.execute("create database db2");
+
+ TestUtils.assertResultSetEqual(
+ statement.executeQuery("count databases"), "count,",
Collections.singleton("3,"));
+ TestUtils.assertResultSetEqual(
+ statement.executeQuery("select count(*) from
information_schema.databases"),
+ "_col0,",
+ Collections.singleton("3,"));
+ }
+ }
+
@Test
public void testDBAuth() throws SQLException {
try (final Connection adminCon =
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
@@ -839,6 +862,8 @@ public class IoTDBDatabaseIT {
try (final Connection userCon =
EnvFactory.getEnv().getConnection("test", "password123456",
BaseEnv.TABLE_SQL_DIALECT);
final Statement userStmt = userCon.createStatement()) {
+ TestUtils.assertResultSetEqual(
+ userStmt.executeQuery("count databases"), "count,",
Collections.singleton("1,"));
TestUtils.assertResultSetEqual(
userStmt.executeQuery("show databases"),
"Database,TTL(ms),SchemaReplicationFactor,DataReplicationFactor,TimePartitionInterval,",
@@ -870,6 +895,8 @@ public class IoTDBDatabaseIT {
try (final Connection userCon =
EnvFactory.getEnv().getConnection("test", "password123456",
BaseEnv.TABLE_SQL_DIALECT);
final Statement userStmt = userCon.createStatement()) {
+ TestUtils.assertResultSetEqual(
+ userStmt.executeQuery("count databases"), "count,",
Collections.singleton("2,"));
try (final ResultSet resultSet = userStmt.executeQuery("SHOW
DATABASES")) {
final ResultSetMetaData metaData = resultSet.getMetaData();
assertEquals(showDBColumnHeaders.size(), metaData.getColumnCount());
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java
index d2e76b218b1..bd1512aa428 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java
@@ -78,6 +78,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterColumnDataType;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction;
@@ -610,6 +611,7 @@ public class Coordinator {
queryContext.setTimeOut(timeOut);
queryContext.setStartTime(startTime);
if (statement instanceof DropDB
+ || statement instanceof CountDB
|| statement instanceof ShowDB
|| statement instanceof CreateDB
|| statement instanceof AlterDB
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
index e07bc920313..827590e86a0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java
@@ -99,6 +99,7 @@ import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.AlterTableRenameTableTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.AlterTableSetPropertiesTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ClearCacheTask;
+import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CountDBTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateDBTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateTableTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateTableViewTask;
@@ -156,6 +157,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateExternalService;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction;
@@ -424,6 +426,15 @@ public class TableConfigTaskVisitor implements
AstVisitor<IConfigTask, MPPQueryC
canShowDB(accessControl, context.getSession().getUserName(),
databaseName, context));
}
+ @Override
+ public IConfigTask visitCountDB(final CountDB node, final MPPQueryContext
context) {
+ context.setQueryType(QueryType.READ);
+ return new CountDBTask(
+ node,
+ databaseName ->
+ canShowDB(accessControl, context.getSession().getUserName(),
databaseName, context));
+ }
+
public static boolean canShowDB(
final AccessControl accessControl,
final String userName,
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
index 3f632c9a987..15a538b98b4 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java
@@ -229,6 +229,7 @@ import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.Ext
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.MigrateRegionTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ReconstructRegionTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.RemoveRegionTask;
+import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CountDBTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DeleteDeviceTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DescribeTableDetailsTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DescribeTableTask;
@@ -255,6 +256,7 @@ import
org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.Sh
import org.apache.iotdb.db.queryengine.plan.expression.Expression;
import
org.apache.iotdb.db.queryengine.plan.expression.visitor.TransformToViewExpressionVisitor;
import
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.view.AlterLogicalViewNode;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCluster;
@@ -4121,7 +4123,7 @@ public class ClusterConfigTaskExecutor implements
IConfigTaskExecutor {
@Override
public SettableFuture<ConfigTaskResult> showDatabases(
- final ShowDB showDB, final Predicate<String> canSeenDB) {
+ final ShowDB showDB, final Predicate<String> canSeeDB) {
final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
// Construct request using statement
final List<String> databasePathPattern = Arrays.asList(ALL_RESULT_NODES);
@@ -4133,7 +4135,25 @@ public class ClusterConfigTaskExecutor implements
IConfigTaskExecutor {
.setIsTableModel(true);
final TShowDatabaseResp resp = client.showDatabase(req);
// build TSBlock
- ShowDBTask.buildTSBlock(resp.getDatabaseInfoMap(), future,
showDB.isDetails(), canSeenDB);
+ ShowDBTask.buildTSBlock(resp.getDatabaseInfoMap(), future,
showDB.isDetails(), canSeeDB);
+ } catch (final IOException | ClientManagerException | TException e) {
+ future.setException(e);
+ }
+ return future;
+ }
+
+ @Override
+ public SettableFuture<ConfigTaskResult> countDatabases(
+ final CountDB countDB, final Predicate<String> canSeeDB) {
+ final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+ final List<String> databasePathPattern = Arrays.asList(ALL_RESULT_NODES);
+ try (final ConfigNodeClient client =
+
CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) {
+ final TGetDatabaseReq req =
+ new TGetDatabaseReq(databasePathPattern, ALL_MATCH_SCOPE.serialize())
+ .setIsTableModel(true);
+ final TShowDatabaseResp resp = client.showDatabase(req);
+ CountDBTask.buildTSBlock(resp.getDatabaseInfoMap(), future, canSeeDB);
} catch (final IOException | ClientManagerException | TException e) {
future.setException(e);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java
index b4b928ba0b6..b45209aeffe 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java
@@ -41,6 +41,7 @@ import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.Mig
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.ReconstructRegionTask;
import
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.region.RemoveRegionTask;
import
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.view.AlterLogicalViewNode;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DeleteDevice;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.DropDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCluster;
@@ -331,7 +332,10 @@ public interface IConfigTaskExecutor {
// =============================== table syntax
=========================================
SettableFuture<ConfigTaskResult> showDatabases(
- final ShowDB showDB, final Predicate<String> canSeenDB);
+ final ShowDB showDB, final Predicate<String> canSeeDB);
+
+ SettableFuture<ConfigTaskResult> countDatabases(
+ final CountDB countDB, final Predicate<String> canSeeDB);
SettableFuture<ConfigTaskResult> showCluster(ShowCluster showCluster);
@@ -355,7 +359,7 @@ public interface IConfigTaskExecutor {
final Boolean isShowCreateView);
SettableFuture<ConfigTaskResult> showTables(
- final String database, final Predicate<String> canSeenDB, final boolean
isDetails);
+ final String database, final Predicate<String> checkCanShowTable, final
boolean isDetails);
TFetchTableResp fetchTables(final Map<String, Set<String>> fetchTableMap);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java
new file mode 100644
index 00000000000..d5605feac18
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTask.java
@@ -0,0 +1,82 @@
+/*
+ * 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.plan.execution.config.metadata.relational;
+
+import org.apache.iotdb.commons.conf.IoTDBConstant;
+import org.apache.iotdb.commons.schema.column.ColumnHeader;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult;
+import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask;
+import
org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.block.TsBlockBuilder;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import static
org.apache.iotdb.commons.schema.table.InformationSchema.INFORMATION_DATABASE;
+
+public class CountDBTask implements IConfigTask {
+
+ private final CountDB node;
+ private final Predicate<String> canSeeDB;
+
+ public CountDBTask(final CountDB node, final Predicate<String> canSeeDB) {
+ this.node = node;
+ this.canSeeDB = canSeeDB;
+ }
+
+ @Override
+ public ListenableFuture<ConfigTaskResult> execute(final IConfigTaskExecutor
configTaskExecutor)
+ throws InterruptedException {
+ return configTaskExecutor.countDatabases(node, canSeeDB);
+ }
+
+ public static void buildTSBlock(
+ final Map<String, ?> databaseInfoMap,
+ final SettableFuture<ConfigTaskResult> future,
+ final Predicate<String> canSeeDB) {
+ // information_schema is synthesized in table model rather than returned
from ConfigNode.
+ final long databaseCount =
+ databaseInfoMap.keySet().stream()
+ .filter(databaseName ->
!INFORMATION_DATABASE.equals(databaseName))
+ .filter(canSeeDB::test)
+ .count()
+ + (canSeeDB.test(INFORMATION_DATABASE) ? 1 : 0);
+
+ final TsBlockBuilder builder = new
TsBlockBuilder(Collections.singletonList(TSDataType.INT32));
+ builder.getTimeColumnBuilder().writeLong(0L);
+ builder.getColumnBuilder(0).writeInt((int) databaseCount);
+ builder.declarePosition();
+
+ final DatasetHeader datasetHeader =
+ new DatasetHeader(
+ Collections.singletonList(
+ new ColumnHeader(IoTDBConstant.COLUMN_COUNT,
TSDataType.INT32)),
+ true);
+ future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS,
builder.build(), datasetHeader));
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java
index f5ab58d3ead..ba8ed54541a 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java
@@ -51,35 +51,35 @@ public class ShowDBTask implements IConfigTask {
private final ShowDB node;
// judge whether the specific database can be seen, dbName should be without
`root.` prefix
- private final Predicate<String> canSeenDB;
+ private final Predicate<String> canSeeDB;
- public ShowDBTask(final ShowDB node, final Predicate<String> canSeenDB) {
+ public ShowDBTask(final ShowDB node, final Predicate<String> canSeeDB) {
this.node = node;
- this.canSeenDB = canSeenDB;
+ this.canSeeDB = canSeeDB;
}
@Override
public ListenableFuture<ConfigTaskResult> execute(final IConfigTaskExecutor
configTaskExecutor)
throws InterruptedException {
- return configTaskExecutor.showDatabases(node, canSeenDB);
+ return configTaskExecutor.showDatabases(node, canSeeDB);
}
public static void buildTSBlock(
final Map<String, TDatabaseInfo> databaseInfoMap,
final SettableFuture<ConfigTaskResult> future,
final boolean isDetails,
- final Predicate<String> canSeenDB) {
+ final Predicate<String> canSeeDB) {
if (isDetails) {
- buildTSBlockForDetails(databaseInfoMap, future, canSeenDB);
+ buildTSBlockForDetails(databaseInfoMap, future, canSeeDB);
} else {
- buildTSBlockForNonDetails(databaseInfoMap, future, canSeenDB);
+ buildTSBlockForNonDetails(databaseInfoMap, future, canSeeDB);
}
}
private static void buildTSBlockForNonDetails(
final Map<String, TDatabaseInfo> databaseInfoMap,
final SettableFuture<ConfigTaskResult> future,
- final Predicate<String> canSeenDB) {
+ final Predicate<String> canSeeDB) {
final List<TSDataType> outputDataTypes =
ColumnHeaderConstant.showDBColumnHeaders.stream()
.map(ColumnHeader::getColumnType)
@@ -93,7 +93,7 @@ public class ShowDBTask implements IConfigTask {
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toList())) {
final String dbName = entry.getKey();
- if (Boolean.FALSE.equals(canSeenDB.test(dbName))) {
+ if (Boolean.FALSE.equals(canSeeDB.test(dbName))) {
continue;
}
if (dbName.equals(INFORMATION_DATABASE)) {
@@ -127,7 +127,7 @@ public class ShowDBTask implements IConfigTask {
private static void buildTSBlockForDetails(
final Map<String, TDatabaseInfo> databaseMap,
final SettableFuture<ConfigTaskResult> future,
- final Predicate<String> canSeenDB) {
+ final Predicate<String> canSeeDB) {
final List<TSDataType> outputDataTypes =
ColumnHeaderConstant.showDBDetailsColumnHeaders.stream()
.map(ColumnHeader::getColumnType)
@@ -141,7 +141,7 @@ public class ShowDBTask implements IConfigTask {
.sorted(Map.Entry.comparingByKey())
.collect(Collectors.toList())) {
final String dbName = entry.getKey();
- if (!canSeenDB.test(dbName)) {
+ if (!canSeeDB.test(dbName)) {
continue;
}
if (dbName.equals(INFORMATION_DATABASE)) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
index 2e236e116e8..7c311fa7f62 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java
@@ -79,6 +79,10 @@ public interface AstVisitor<R, C> extends
CommonQueryAstVisitor<R, C> {
return visitStatement(node, context);
}
+ default R visitCountDB(final CountDB node, final C context) {
+ return visitStatement(node, context);
+ }
+
default R visitCreateTable(final CreateTable node, final C context) {
return visitStatement(node, context);
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java
new file mode 100644
index 00000000000..49f113421b2
--- /dev/null
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDB.java
@@ -0,0 +1,77 @@
+/*
+ * 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.plan.relational.sql.ast;
+
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.AstMemoryEstimationHelper;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation;
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.tsfile.utils.RamUsageEstimator;
+
+import java.util.List;
+
+import static java.util.Objects.requireNonNull;
+
+public class CountDB extends Statement {
+
+ private static final long INSTANCE_SIZE =
RamUsageEstimator.shallowSizeOfInstance(CountDB.class);
+
+ public CountDB(final NodeLocation location) {
+ super(requireNonNull(location, "location is null"));
+ }
+
+ @Override
+ public <R, C> R accept(final IAstVisitor<R, C> visitor, final C context) {
+ return ((AstVisitor<R, C>) visitor).visitCountDB(this, context);
+ }
+
+ @Override
+ public List<Node> getChildren() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public int hashCode() {
+ return getClass().hashCode();
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ return (obj != null) && (getClass() == obj.getClass());
+ }
+
+ @Override
+ public String toString() {
+ return "COUNT DATABASES";
+ }
+
+ @Override
+ public long ramBytesUsed() {
+ long size = INSTANCE_SIZE;
+ size +=
AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal());
+ return size;
+ }
+}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
index 768a05689e8..25d68eba6be 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java
@@ -162,6 +162,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AsofJoinOn;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CopyTo;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountStatement;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
@@ -406,6 +407,12 @@ public class AstBuilder extends
RelationalSqlBaseVisitor<Node> {
return new ShowDB(getLocation(ctx), Objects.nonNull(ctx.DETAILS()));
}
+ @Override
+ public Node visitCountDatabasesStatement(
+ final RelationalSqlParser.CountDatabasesStatementContext ctx) {
+ return new CountDB(getLocation(ctx));
+ }
+
@Override
public Node visitCreateDbStatement(final
RelationalSqlParser.CreateDbStatementContext ctx) {
List<Property> properties = ImmutableList.of();
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
index 4797d43cf21..b806fd72592 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java
@@ -32,6 +32,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
import
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CopyTo;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateFunction;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreatePipe;
@@ -134,7 +135,13 @@ public final class DataNodeSqlFormatter extends
CommonQuerySqlFormatter
@Override
public Void visitShowDB(ShowDB node, Integer indent) {
- builder.append("SHOW DATABASE");
+ builder.append("SHOW DATABASES");
+ return null;
+ }
+
+ @Override
+ public Void visitCountDB(CountDB node, Integer indent) {
+ builder.append("COUNT DATABASES");
return null;
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java
new file mode 100644
index 00000000000..b29c11c2a91
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/CountDBTaskTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.plan.execution.config.metadata.relational;
+
+import org.apache.iotdb.commons.conf.IoTDBConstant;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation;
+import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult;
+import
org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.tsfile.read.common.block.TsBlock;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import static
org.apache.iotdb.commons.schema.table.InformationSchema.INFORMATION_DATABASE;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.mockito.ArgumentMatchers.same;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+public class CountDBTaskTest {
+
+ @Test
+ public void testBuildTSBlockCountsVisibleDatabases() throws Exception {
+ final Map<String, Object> databaseInfoMap = new HashMap<>();
+ databaseInfoMap.put("db1", new Object());
+ databaseInfoMap.put("db2", new Object());
+
+ final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+ CountDBTask.buildTSBlock(databaseInfoMap, future, databaseName ->
!"db2".equals(databaseName));
+
+ final ConfigTaskResult result = future.get();
+ final TsBlock resultSet = result.getResultSet();
+
+ assertEquals(TSStatusCode.SUCCESS_STATUS, result.getStatusCode());
+ assertEquals(
+ Collections.singletonList(IoTDBConstant.COLUMN_COUNT),
+ result.getResultSetHeader().getRespColumns());
+ assertEquals(1, resultSet.getPositionCount());
+ assertEquals(2, resultSet.getColumn(0).getInt(0));
+ }
+
+ @Test
+ public void testBuildTSBlockCanHideInformationSchema() throws Exception {
+ final Map<String, Object> databaseInfoMap = new HashMap<>();
+ databaseInfoMap.put("db1", new Object());
+ databaseInfoMap.put("db2", new Object());
+
+ final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+ CountDBTask.buildTSBlock(databaseInfoMap, future, databaseName ->
"db1".equals(databaseName));
+
+ final ConfigTaskResult result = future.get();
+ assertEquals(1, result.getResultSet().getColumn(0).getInt(0));
+ }
+
+ @Test
+ public void testBuildTSBlockDoesNotDoubleCountInformationSchema() throws
Exception {
+ final Map<String, Object> databaseInfoMap = new HashMap<>();
+ databaseInfoMap.put("db1", new Object());
+ databaseInfoMap.put(INFORMATION_DATABASE, new Object());
+
+ final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+ CountDBTask.buildTSBlock(databaseInfoMap, future, databaseName -> true);
+
+ final ConfigTaskResult result = future.get();
+ assertEquals(2, result.getResultSet().getColumn(0).getInt(0));
+ }
+
+ @Test
+ public void testExecuteDelegatesToExecutor() throws Exception {
+ final CountDB node = new CountDB(new NodeLocation(1, 1));
+ final Predicate<String> canSeenDB = databaseName -> true;
+ final CountDBTask task = new CountDBTask(node, canSeenDB);
+ final IConfigTaskExecutor executor =
Mockito.mock(IConfigTaskExecutor.class);
+ final SettableFuture<ConfigTaskResult> future = SettableFuture.create();
+
+ when(executor.countDatabases(same(node),
same(canSeenDB))).thenReturn(future);
+
+ assertSame(future, task.execute(executor));
+ verify(executor).countDatabases(same(node), same(canSeenDB));
+ }
+}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/CountDBStatementTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/CountDBStatementTest.java
new file mode 100644
index 00000000000..fa36bcd1bf8
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/CountDBStatementTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.plan.relational.sql.parser;
+
+import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement;
+import
org.apache.iotdb.commons.queryengine.plan.relational.sql.parser.ParsingException;
+import org.apache.iotdb.db.protocol.session.IClientSession;
+import org.apache.iotdb.db.protocol.session.InternalClientSession;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDB;
+import
org.apache.iotdb.db.queryengine.plan.relational.sql.util.DataNodeSqlFormatter;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.ZoneId;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+public class CountDBStatementTest {
+
+ private SqlParser sqlParser;
+ private IClientSession clientSession;
+
+ @Before
+ public void setUp() {
+ sqlParser = new SqlParser();
+ clientSession = new InternalClientSession("testClient");
+ }
+
+ @Test
+ public void testCountDatabaseStatementRejected() {
+ assertThrows(
+ ParsingException.class,
+ () -> sqlParser.createStatement("count database",
ZoneId.systemDefault(), clientSession));
+ }
+
+ @Test
+ public void testCountDatabasesStatement() {
+ final Statement statement =
+ sqlParser.createStatement("count databases", ZoneId.systemDefault(),
clientSession);
+
+ assertTrue(statement instanceof CountDB);
+ assertEquals("COUNT DATABASES", statement.toString());
+ assertEquals("COUNT DATABASES",
DataNodeSqlFormatter.formatDataNodeSql(statement));
+ }
+}
diff --git
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
index ca766464686..f984e825fc7 100644
---
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
+++
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
@@ -49,6 +49,7 @@ statement
// Database Statement
| useDatabaseStatement
| showDatabasesStatement
+ | countDatabasesStatement
| createDbStatement
| alterDbStatement
| dropDbStatement
@@ -203,6 +204,10 @@ showDatabasesStatement
: SHOW DATABASES (DETAILS)?
;
+countDatabasesStatement
+ : COUNT DATABASES
+ ;
+
createDbStatement
: CREATE DATABASE (IF NOT EXISTS)? database=identifier (WITH properties)?
;