This is an automated email from the ASF dual-hosted git repository.

jackietien pushed a commit to branch ty/TableModelGrammar
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/ty/TableModelGrammar by this 
push:
     new 0b9acacaa55 Support basic Show / Count devices function
0b9acacaa55 is described below

commit 0b9acacaa55dc4d0667285f68362565e5060e352
Author: Caideyipi <[email protected]>
AuthorDate: Mon Jul 29 11:19:51 2024 +0800

    Support basic Show / Count devices function
---
 .../relational/it/schema/IoTDBDeviceQueryIT.java   | 101 ++++++++++++++++
 .../iotdb/relational/it/schema/IoTDBTableIT.java   |   4 +-
 .../metadata/table/TableNotExistsException.java    |   2 +-
 .../schema/source/TableDeviceQuerySource.java      |   9 +-
 .../queryengine/plan/execution/QueryExecution.java |   4 +-
 .../plan/planner/TableOperatorGenerator.java       |  54 ++++++++-
 .../plan/planner/plan/node/PlanNodeType.java       |  10 +-
 .../plan/planner/plan/node/PlanVisitor.java        |   9 +-
 ...Node.java => AbstractTableDeviceQueryNode.java} | 129 +++++++++-----------
 .../metedata/read/TableDeviceQueryCountNode.java   |  83 +++++++++++++
 .../metedata/read/TableDeviceQueryScanNode.java    |  83 +++++++++++++
 .../relational/analyzer/StatementAnalyzer.java     |  15 ++-
 .../plan/relational/planner/LogicalPlanner.java    |  77 ++++++++++--
 .../planner/distribute/AddExchangeNodes.java       |  24 ++--
 .../distribute/TableDistributedPlanGenerator.java  |  68 +++++++----
 .../{ShowDevice.java => AbstractQueryDevice.java}  |  45 +++----
 .../plan/relational/sql/ast/AstVisitor.java        |   4 +
 .../plan/relational/sql/ast/CountDevice.java}      |  22 ++--
 .../plan/relational/sql/ast/ShowDevice.java        | 134 ++-------------------
 .../plan/relational/sql/parser/AstBuilder.java     |  18 ++-
 .../db/relational/grammar/sql/RelationalSql.g4     |   4 +-
 21 files changed, 598 insertions(+), 301 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceQueryIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceQueryIT.java
new file mode 100644
index 00000000000..49fb045f1e0
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDeviceQueryIT.java
@@ -0,0 +1,101 @@
+/*
+ * 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.relational.it.schema;
+
+import org.apache.iotdb.db.it.utils.TestUtils;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.ClusterIT;
+import org.apache.iotdb.itbase.category.LocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Collections;
+
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({LocalStandaloneIT.class, ClusterIT.class})
+public class IoTDBDeviceQueryIT {
+  @BeforeClass
+  public static void setUp() throws Exception {
+    EnvFactory.getEnv().initClusterEnvironment();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testQueryDevice() throws SQLException {
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database test");
+      statement.execute("use test");
+      statement.execute(
+          "create table table1(region_id STRING ID, plant_id STRING ID, 
device_id STRING ID, model STRING ATTRIBUTE, temperature FLOAT MEASUREMENT, 
humidity DOUBLE MEASUREMENT)");
+      statement.execute(
+          "create table table0(region_id STRING ID, plant_id STRING ID, 
device_id STRING ID, model STRING ATTRIBUTE, temperature FLOAT MEASUREMENT, 
humidity DOUBLE MEASUREMENT)");
+      statement.execute(
+          "insert into table0(region_id, plant_id, device_id, model, 
temperature, humidity) values('1', '5', '3', 'A', 37.6, 111.1)");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from table0"),
+          "region_id,plant_id,device_id,model,",
+          Collections.singleton("1,5,3,A,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("count devices from table0"),
+          "count(devices),",
+          Collections.singleton("1,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from table1"),
+          "region_id,plant_id,device_id,model,",
+          Collections.emptySet());
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("count devices from table1"),
+          "count(devices),",
+          Collections.singleton("0,"));
+
+      try {
+        statement.executeQuery("show devices from table2");
+        fail("Show devices shall fail for non-exist table");
+      } catch (final Exception e) {
+        // Pass
+      }
+
+      try {
+        statement.executeQuery("count devices from table2");
+        fail("Count devices shall fail for non-exist table");
+      } catch (final Exception e) {
+        // Pass
+      }
+    }
+  }
+}
diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
index d96088fb2e9..1063d831ebb 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
@@ -197,7 +197,7 @@ public class IoTDBTableIT {
       try {
         statement.executeQuery("describe table1");
       } catch (final SQLException e) {
-        assertEquals("550: Table test2.table1 not exists.", e.getMessage());
+        assertEquals("550: Table 'test2.table1' does not exist.", 
e.getMessage());
       }
 
       String[] columnNames =
@@ -250,7 +250,7 @@ public class IoTDBTableIT {
       try {
         statement.executeQuery("describe test3.table3");
       } catch (final SQLException e) {
-        assertEquals("550: Table test3.table3 not exists.", e.getMessage());
+        assertEquals("550: Table 'test3.table3' does not exist.", 
e.getMessage());
       }
 
       statement.execute("drop database test1");
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
index 35ec8873ba4..1bc2db0123c 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
@@ -26,7 +26,7 @@ public class TableNotExistsException extends 
MetadataException {
 
   public TableNotExistsException(String database, String tableName) {
     super(
-        String.format("Table %s.%s not exists.", database, tableName),
+        String.format("Table '%s.%s' does not exist.", database, tableName),
         TSStatusCode.TABLE_NOT_EXISTS.getStatusCode());
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java
index b745115d06b..88b73633517 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/schema/source/TableDeviceQuerySource.java
@@ -43,6 +43,7 @@ import org.apache.tsfile.utils.Binary;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 
 public class TableDeviceQuerySource implements 
ISchemaSource<IDeviceSchemaInfo> {
 
@@ -157,6 +158,10 @@ public class TableDeviceQuerySource implements 
ISchemaSource<IDeviceSchemaInfo>
   }
 
   private List<PartialPath> getDevicePatternList() {
+    if (Objects.isNull(DataNodeTableCache.getInstance().getTable(database, 
tableName))) {
+      throw new SchemaExecutionException(
+          String.format("Table '%s.%s' does not exist.", database, tableName));
+    }
     return DeviceFilterUtil.convertToDevicePattern(
         database,
         tableName,
@@ -233,12 +238,12 @@ public class TableDeviceQuerySource implements 
ISchemaSource<IDeviceSchemaInfo>
   }
 
   @Override
-  public boolean hasSchemaStatistic(ISchemaRegion schemaRegion) {
+  public boolean hasSchemaStatistic(final ISchemaRegion schemaRegion) {
     return false;
   }
 
   @Override
-  public long getSchemaStatistic(ISchemaRegion schemaRegion) {
+  public long getSchemaStatistic(final ISchemaRegion schemaRegion) {
     return 0;
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java
index 21c2cc2111e..caa7b046507 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/QueryExecution.java
@@ -154,7 +154,7 @@ public class QueryExecution implements IQueryExecution {
     final long startTime = System.nanoTime();
     if (skipExecute()) {
       LOGGER.debug("[SkipExecute]");
-      if (context.getQueryType() == QueryType.WRITE && analysis.isFailed()) {
+      if (analysis.isFailed()) {
         stateMachine.transitionToFailed(analysis.getFailStatus());
       } else {
         constructResultForMemorySource();
@@ -170,7 +170,7 @@ public class QueryExecution implements IQueryExecution {
     if (skipExecute()) {
       // TODO move this judgement to analyze state?
       LOGGER.debug("[SkipExecute After LogicalPlan]");
-      if (context.getQueryType() == QueryType.WRITE && analysis.isFailed()) {
+      if (analysis.isFailed()) {
         stateMachine.transitionToFailed(analysis.getFailStatus());
       } else {
         constructResultForMemorySource();
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
index 3c9af7b597f..052614f9062 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/TableOperatorGenerator.java
@@ -40,6 +40,8 @@ import 
org.apache.iotdb.db.queryengine.execution.operator.process.TableMergeSort
 import 
org.apache.iotdb.db.queryengine.execution.operator.process.TableSortOperator;
 import 
org.apache.iotdb.db.queryengine.execution.operator.process.TableStreamSortOperator;
 import 
org.apache.iotdb.db.queryengine.execution.operator.process.TableTopKOperator;
+import 
org.apache.iotdb.db.queryengine.execution.operator.schema.CountMergeOperator;
+import 
org.apache.iotdb.db.queryengine.execution.operator.schema.SchemaCountOperator;
 import 
org.apache.iotdb.db.queryengine.execution.operator.schema.SchemaQueryScanOperator;
 import 
org.apache.iotdb.db.queryengine.execution.operator.schema.source.SchemaSourceFactory;
 import 
org.apache.iotdb.db.queryengine.execution.operator.sink.IdentitySinkOperator;
@@ -51,8 +53,10 @@ import 
org.apache.iotdb.db.queryengine.plan.analyze.TypeProvider;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.CountSchemaMergeNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.ExchangeNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.sink.IdentitySinkNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.InputLocation;
@@ -752,10 +756,27 @@ public class TableOperatorGenerator extends 
PlanVisitor<Operator, LocalExecution
         TSFileDescriptor.getInstance().getConfig().getMaxTsBlockLineNumber());
   }
 
+  @Override
+  public Operator visitCountMerge(
+      final CountSchemaMergeNode node, final LocalExecutionPlanContext 
context) {
+    final OperatorContext operatorContext =
+        context
+            .getDriverContext()
+            .addOperatorContext(
+                context.getNextOperatorId(),
+                node.getPlanNodeId(),
+                CountMergeOperator.class.getSimpleName());
+    final List<Operator> children = new ArrayList<>(node.getChildren().size());
+    for (final PlanNode child : node.getChildren()) {
+      children.add(this.process(child, context));
+    }
+    return new CountMergeOperator(operatorContext, children);
+  }
+
   @Override
   public Operator visitTableDeviceFetch(
-      TableDeviceFetchNode node, LocalExecutionPlanContext context) {
-    OperatorContext operatorContext =
+      final TableDeviceFetchNode node, final LocalExecutionPlanContext 
context) {
+    final OperatorContext operatorContext =
         context
             .getDriverContext()
             .addOperatorContext(
@@ -773,9 +794,9 @@ public class TableOperatorGenerator extends 
PlanVisitor<Operator, LocalExecution
   }
 
   @Override
-  public Operator visitTableDeviceQuery(
-      TableDeviceQueryNode node, LocalExecutionPlanContext context) {
-    OperatorContext operatorContext =
+  public Operator visitTableDeviceQueryScan(
+      final TableDeviceQueryScanNode node, final LocalExecutionPlanContext 
context) {
+    final OperatorContext operatorContext =
         context
             .getDriverContext()
             .addOperatorContext(
@@ -792,4 +813,25 @@ public class TableOperatorGenerator extends 
PlanVisitor<Operator, LocalExecution
             node.getIdFuzzyPredicate(),
             node.getColumnHeaderList()));
   }
+
+  @Override
+  public Operator visitTableDeviceQueryCount(
+      final TableDeviceQueryCountNode node, final LocalExecutionPlanContext 
context) {
+    final OperatorContext operatorContext =
+        context
+            .getDriverContext()
+            .addOperatorContext(
+                context.getNextOperatorId(),
+                node.getPlanNodeId(),
+                SchemaCountOperator.class.getSimpleName());
+    return new SchemaCountOperator<>(
+        node.getPlanNodeId(),
+        operatorContext,
+        SchemaSourceFactory.getTableDeviceQuerySource(
+            node.getDatabase(),
+            node.getTableName(),
+            node.getIdDeterminedPredicateList(),
+            node.getIdFuzzyPredicate(),
+            node.getColumnHeaderList()));
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNodeType.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNodeType.java
index bb7dd0d14e3..64ffc62285f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNodeType.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanNodeType.java
@@ -36,7 +36,8 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.Sche
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.SchemaQueryOrderByHeatNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.SeriesSchemaFetchScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TimeSeriesCountNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.write.ActivateTemplateNode;
@@ -231,9 +232,10 @@ public enum PlanNodeType {
   CONTINUOUS_SAME_SEARCH_INDEX_SEPARATOR((short) 97),
 
   CREATE_TABLE_DEVICE((short) 902),
-  TABLE_DEVICE_QUERY((short) 903),
+  TABLE_DEVICE_QUERY_SCAN((short) 903),
   TABLE_DEVICE_FETCH((short) 904),
   DELETE_TABLE_DEVICE((short) 905),
+  TABLE_DEVICE_QUERY_COUNT((short) 906),
 
   TABLE_SCAN_NODE((short) 1000),
   TABLE_FILTER_NODE((short) 1001),
@@ -510,9 +512,11 @@ public enum PlanNodeType {
       case 902:
         return CreateTableDeviceNode.deserialize(buffer);
       case 903:
-        return TableDeviceQueryNode.deserialize(buffer);
+        return TableDeviceQueryScanNode.deserialize(buffer);
       case 904:
         return TableDeviceFetchNode.deserialize(buffer);
+      case 906:
+        return TableDeviceQueryCountNode.deserialize(buffer);
       case 1000:
         return 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode
             .deserialize(buffer);
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
index 1ee8ec8b6ac..d5e2f5d26a3 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanVisitor.java
@@ -33,7 +33,8 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.Sche
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.SchemaQueryScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.SeriesSchemaFetchScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TimeSeriesCountNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TimeSeriesSchemaScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.write.ActivateTemplateNode;
@@ -502,7 +503,11 @@ public abstract class PlanVisitor<R, C> {
     return visitPlan(node, context);
   }
 
-  public R visitTableDeviceQuery(TableDeviceQueryNode node, C context) {
+  public R visitTableDeviceQueryScan(TableDeviceQueryScanNode node, C context) 
{
+    return visitPlan(node, context);
+  }
+
+  public R visitTableDeviceQueryCount(TableDeviceQueryCountNode node, C 
context) {
     return visitPlan(node, context);
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/AbstractTableDeviceQueryNode.java
similarity index 65%
rename from 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryNode.java
rename to 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/AbstractTableDeviceQueryNode.java
index 77b936a7885..785514e67b4 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryNode.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/AbstractTableDeviceQueryNode.java
@@ -23,8 +23,6 @@ import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
-import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeType;
-import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
 
 import org.apache.tsfile.utils.ReadWriteIOUtils;
@@ -36,7 +34,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Objects;
 
-public class TableDeviceQueryNode extends TableDeviceSourceNode {
+public abstract class AbstractTableDeviceQueryNode extends 
TableDeviceSourceNode {
 
   /**
    * The outer list represents the OR relation between different expression 
lists.
@@ -46,19 +44,19 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
    * <p>Each inner list represents a device pattern and each expression of it 
represents one
    * condition on some id column.
    */
-  private final List<List<Expression>> idDeterminedPredicateList;
+  protected final List<List<Expression>> idDeterminedPredicateList;
 
   /** filters/conditions involving non-id columns and concat by OR to id 
column filters */
-  private final Expression idFuzzyPredicate;
-
-  public TableDeviceQueryNode(
-      PlanNodeId planNodeId,
-      String database,
-      String tableName,
-      List<List<Expression>> idDeterminedPredicateList,
-      Expression idFuzzyPredicate,
-      List<ColumnHeader> columnHeaderList,
-      TRegionReplicaSet schemaRegionReplicaSet) {
+  protected final Expression idFuzzyPredicate;
+
+  protected AbstractTableDeviceQueryNode(
+      final PlanNodeId planNodeId,
+      final String database,
+      final String tableName,
+      final List<List<Expression>> idDeterminedPredicateList,
+      final Expression idFuzzyPredicate,
+      final List<ColumnHeader> columnHeaderList,
+      final TRegionReplicaSet schemaRegionReplicaSet) {
     super(planNodeId, database, tableName, columnHeaderList, 
schemaRegionReplicaSet);
     this.idDeterminedPredicateList = idDeterminedPredicateList;
     this.idFuzzyPredicate = idFuzzyPredicate;
@@ -73,37 +71,15 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
   }
 
   @Override
-  public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
-    return visitor.visitTableDeviceQuery(this, context);
-  }
-
-  @Override
-  public PlanNodeType getType() {
-    return PlanNodeType.TABLE_DEVICE_QUERY;
-  }
-
-  @Override
-  public PlanNode clone() {
-    return new TableDeviceQueryNode(
-        getPlanNodeId(),
-        database,
-        tableName,
-        idDeterminedPredicateList,
-        idFuzzyPredicate,
-        columnHeaderList,
-        schemaRegionReplicaSet);
-  }
-
-  @Override
-  protected void serializeAttributes(ByteBuffer byteBuffer) {
-    PlanNodeType.TABLE_DEVICE_QUERY.serialize(byteBuffer);
+  protected void serializeAttributes(final ByteBuffer byteBuffer) {
+    getType().serialize(byteBuffer);
     ReadWriteIOUtils.write(database, byteBuffer);
     ReadWriteIOUtils.write(tableName, byteBuffer);
 
     ReadWriteIOUtils.write(idDeterminedPredicateList.size(), byteBuffer);
-    for (List<Expression> filterList : idDeterminedPredicateList) {
+    for (final List<Expression> filterList : idDeterminedPredicateList) {
       ReadWriteIOUtils.write(filterList.size(), byteBuffer);
-      for (Expression expression : filterList) {
+      for (final Expression expression : filterList) {
         Expression.serialize(expression, byteBuffer);
       }
     }
@@ -114,21 +90,21 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
     }
 
     ReadWriteIOUtils.write(columnHeaderList.size(), byteBuffer);
-    for (ColumnHeader columnHeader : columnHeaderList) {
+    for (final ColumnHeader columnHeader : columnHeaderList) {
       columnHeader.serialize(byteBuffer);
     }
   }
 
   @Override
-  protected void serializeAttributes(DataOutputStream stream) throws 
IOException {
-    PlanNodeType.TABLE_DEVICE_QUERY.serialize(stream);
+  protected void serializeAttributes(final DataOutputStream stream) throws 
IOException {
+    getType().serialize(stream);
     ReadWriteIOUtils.write(database, stream);
     ReadWriteIOUtils.write(tableName, stream);
 
     ReadWriteIOUtils.write(idDeterminedPredicateList.size(), stream);
-    for (List<Expression> filterList : idDeterminedPredicateList) {
+    for (final List<Expression> filterList : idDeterminedPredicateList) {
       ReadWriteIOUtils.write(filterList.size(), stream);
-      for (Expression expression : filterList) {
+      for (final Expression expression : filterList) {
         Expression.serialize(expression, stream);
       }
     }
@@ -139,19 +115,19 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
     }
 
     ReadWriteIOUtils.write(columnHeaderList.size(), stream);
-    for (ColumnHeader columnHeader : columnHeaderList) {
+    for (final ColumnHeader columnHeader : columnHeaderList) {
       columnHeader.serialize(stream);
     }
   }
 
-  public static PlanNode deserialize(ByteBuffer buffer) {
-    String database = ReadWriteIOUtils.readString(buffer);
-    String tableName = ReadWriteIOUtils.readString(buffer);
+  protected static PlanNode deserialize(final ByteBuffer buffer, final boolean 
isScan) {
+    final String database = ReadWriteIOUtils.readString(buffer);
+    final String tableName = ReadWriteIOUtils.readString(buffer);
 
     int size = ReadWriteIOUtils.readInt(buffer);
-    List<List<Expression>> idDeterminedFilterList = new ArrayList<>(size);
+    final List<List<Expression>> idDeterminedFilterList = new 
ArrayList<>(size);
     for (int i = 0; i < size; i++) {
-      int singleSize = ReadWriteIOUtils.readInt(buffer);
+      final int singleSize = ReadWriteIOUtils.readInt(buffer);
       idDeterminedFilterList.add(new ArrayList<>(singleSize));
       for (int k = 0; k < singleSize; k++) {
         idDeterminedFilterList.get(i).add(Expression.deserialize(buffer));
@@ -164,28 +140,43 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
     }
 
     size = ReadWriteIOUtils.readInt(buffer);
-    List<ColumnHeader> columnHeaderList = new ArrayList<>(size);
+    final List<ColumnHeader> columnHeaderList = new ArrayList<>(size);
     for (int i = 0; i < size; i++) {
       columnHeaderList.add(ColumnHeader.deserialize(buffer));
     }
 
-    PlanNodeId planNodeId = PlanNodeId.deserialize(buffer);
-    return new TableDeviceQueryNode(
-        planNodeId,
-        database,
-        tableName,
-        idDeterminedFilterList,
-        idFuzzyFilter,
-        columnHeaderList,
-        null);
+    final PlanNodeId planNodeId = PlanNodeId.deserialize(buffer);
+    return isScan
+        ? new TableDeviceQueryScanNode(
+            planNodeId,
+            database,
+            tableName,
+            idDeterminedFilterList,
+            idFuzzyFilter,
+            columnHeaderList,
+            null)
+        : new TableDeviceQueryCountNode(
+            planNodeId,
+            database,
+            tableName,
+            idDeterminedFilterList,
+            idFuzzyFilter,
+            columnHeaderList,
+            null);
   }
 
   @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    if (!super.equals(o)) return false;
-    TableDeviceQueryNode that = (TableDeviceQueryNode) o;
+  public boolean equals(final Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+    if (!super.equals(o)) {
+      return false;
+    }
+    final AbstractTableDeviceQueryNode that = (AbstractTableDeviceQueryNode) o;
     return Objects.equals(database, that.database)
         && Objects.equals(tableName, that.tableName)
         && Objects.equals(idDeterminedPredicateList, 
that.idDeterminedPredicateList)
@@ -206,10 +197,8 @@ public class TableDeviceQueryNode extends 
TableDeviceSourceNode {
         schemaRegionReplicaSet);
   }
 
-  @Override
-  public String toString() {
-    return "TableDeviceQueryNode{"
-        + "database='"
+  protected String toStringMessage() {
+    return "{database='"
         + database
         + '\''
         + ", tableName='"
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryCountNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryCountNode.java
new file mode 100644
index 00000000000..a43d4fc5cf2
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryCountNode.java
@@ -0,0 +1,83 @@
+/*
+ * 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.planner.plan.node.metedata.read;
+
+import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeType;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class TableDeviceQueryCountNode extends AbstractTableDeviceQueryNode {
+
+  public TableDeviceQueryCountNode(
+      final PlanNodeId planNodeId,
+      final String database,
+      final String tableName,
+      final List<List<Expression>> idDeterminedPredicateList,
+      final Expression idFuzzyPredicate,
+      final List<ColumnHeader> columnHeaderList,
+      final TRegionReplicaSet schemaRegionReplicaSet) {
+    super(
+        planNodeId,
+        database,
+        tableName,
+        idDeterminedPredicateList,
+        idFuzzyPredicate,
+        columnHeaderList,
+        schemaRegionReplicaSet);
+  }
+
+  @Override
+  public <R, C> R accept(final PlanVisitor<R, C> visitor, final C context) {
+    return visitor.visitTableDeviceQueryCount(this, context);
+  }
+
+  @Override
+  public PlanNodeType getType() {
+    return PlanNodeType.TABLE_DEVICE_QUERY_COUNT;
+  }
+
+  @Override
+  public PlanNode clone() {
+    return new TableDeviceQueryCountNode(
+        getPlanNodeId(),
+        database,
+        tableName,
+        idDeterminedPredicateList,
+        idFuzzyPredicate,
+        columnHeaderList,
+        schemaRegionReplicaSet);
+  }
+
+  public static PlanNode deserialize(final ByteBuffer buffer) {
+    return AbstractTableDeviceQueryNode.deserialize(buffer, false);
+  }
+
+  @Override
+  public String toString() {
+    return "TableDeviceQueryCountNode" + toStringMessage();
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryScanNode.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryScanNode.java
new file mode 100644
index 00000000000..b3360a1b03f
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metedata/read/TableDeviceQueryScanNode.java
@@ -0,0 +1,83 @@
+/*
+ * 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.planner.plan.node.metedata.read;
+
+import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeType;
+import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression;
+
+import java.nio.ByteBuffer;
+import java.util.List;
+
+public class TableDeviceQueryScanNode extends AbstractTableDeviceQueryNode {
+
+  public TableDeviceQueryScanNode(
+      final PlanNodeId planNodeId,
+      final String database,
+      final String tableName,
+      final List<List<Expression>> idDeterminedPredicateList,
+      final Expression idFuzzyPredicate,
+      final List<ColumnHeader> columnHeaderList,
+      final TRegionReplicaSet schemaRegionReplicaSet) {
+    super(
+        planNodeId,
+        database,
+        tableName,
+        idDeterminedPredicateList,
+        idFuzzyPredicate,
+        columnHeaderList,
+        schemaRegionReplicaSet);
+  }
+
+  @Override
+  public <R, C> R accept(final PlanVisitor<R, C> visitor, final C context) {
+    return visitor.visitTableDeviceQueryScan(this, context);
+  }
+
+  @Override
+  public PlanNodeType getType() {
+    return PlanNodeType.TABLE_DEVICE_QUERY_SCAN;
+  }
+
+  @Override
+  public PlanNode clone() {
+    return new TableDeviceQueryScanNode(
+        getPlanNodeId(),
+        database,
+        tableName,
+        idDeterminedPredicateList,
+        idFuzzyPredicate,
+        columnHeaderList,
+        schemaRegionReplicaSet);
+  }
+
+  public static PlanNode deserialize(final ByteBuffer buffer) {
+    return AbstractTableDeviceQueryNode.deserialize(buffer, true);
+  }
+
+  @Override
+  public String toString() {
+    return "TableDeviceQueryScanNode" + toStringMessage();
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
index 57d10e46fd0..b2d3532c717 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
@@ -38,6 +38,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AliasedRelation;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AllColumns;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AllRows;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateIndex;
@@ -245,7 +246,8 @@ public class StatementAnalyzer {
       Scope returnScope = super.process(node, scope);
       if (node instanceof CreateDevice
           || node instanceof FetchDevice
-          || node instanceof ShowDevice) {
+          || node instanceof ShowDevice
+          || node instanceof CountDevice) {
         return returnScope;
       }
       checkState(
@@ -2474,17 +2476,22 @@ public class StatementAnalyzer {
     }
 
     @Override
-    protected Scope visitCreateDevice(CreateDevice node, Optional<Scope> 
context) {
+    protected Scope visitCreateDevice(final CreateDevice node, final 
Optional<Scope> context) {
       return null;
     }
 
     @Override
-    protected Scope visitFetchDevice(FetchDevice node, Optional<Scope> 
context) {
+    protected Scope visitFetchDevice(final FetchDevice node, final 
Optional<Scope> context) {
       return null;
     }
 
     @Override
-    protected Scope visitShowDevice(ShowDevice node, Optional<Scope> context) {
+    protected Scope visitShowDevice(final ShowDevice node, final 
Optional<Scope> context) {
+      return null;
+    }
+
+    @Override
+    protected Scope visitCountDevice(final CountDevice node, final 
Optional<Scope> context) {
       return null;
     }
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
index a906cd53e31..7ed90b1347c 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/LogicalPlanner.java
@@ -17,6 +17,7 @@ package 
org.apache.iotdb.db.queryengine.plan.relational.planner;
 import org.apache.iotdb.commons.partition.SchemaPartition;
 import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
 import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
+import org.apache.iotdb.db.exception.sql.SemanticException;
 import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
 import org.apache.iotdb.db.queryengine.common.SessionInfo;
 import org.apache.iotdb.db.queryengine.common.header.ColumnHeader;
@@ -26,8 +27,10 @@ import 
org.apache.iotdb.db.queryengine.plan.analyze.QueryType;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.CountSchemaMergeNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Field;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.RelationType;
@@ -37,6 +40,8 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.CreateTableD
 import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.OptimizeFactory;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanOptimizer;
+import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AbstractQueryDevice;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Explain;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.FetchDevice;
@@ -50,11 +55,14 @@ import 
org.apache.iotdb.db.schemaengine.table.DataNodeTableCache;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
+import org.apache.tsfile.enums.TSDataType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Objects;
 
 import static java.util.Objects.requireNonNull;
 import static 
org.apache.iotdb.db.queryengine.plan.relational.type.InternalTypeManager.getTSDataType;
@@ -106,7 +114,7 @@ public class LogicalPlanner {
     return new LogicalQueryPlan(queryContext, planNode);
   }
 
-  private PlanNode planStatement(Analysis analysis, Statement statement) {
+  private PlanNode planStatement(final Analysis analysis, final Statement 
statement) {
     if (statement instanceof CreateDevice) {
       return planCreateDevice((CreateDevice) statement, analysis);
     }
@@ -116,6 +124,9 @@ public class LogicalPlanner {
     if (statement instanceof ShowDevice) {
       return planShowDevice((ShowDevice) statement, analysis);
     }
+    if (statement instanceof CountDevice) {
+      return planCountDevice((CountDevice) statement, analysis);
+    }
     return createOutputPlan(planStatementWithoutOutput(analysis, statement), 
analysis);
   }
 
@@ -238,35 +249,75 @@ public class LogicalPlanner {
     return fetchNode;
   }
 
-  private PlanNode planShowDevice(ShowDevice statement, Analysis analysis) {
-    queryContext.setQueryType(QueryType.READ);
+  private PlanNode planShowDevice(final ShowDevice statement, final Analysis 
analysis) {
+    final String database = planQueryDevice(statement, analysis);
+    List<ColumnHeader> columnHeaderList = null;
+    if (!analysis.isFailed()) {
+      columnHeaderList = getColumnHeaderList(database, 
statement.getTableName());
+      analysis.setRespDatasetHeader(
+          new DatasetHeader(getColumnHeaderList(database, 
statement.getTableName()), true));
+    }
 
-    List<ColumnHeader> columnHeaderList =
-        getColumnHeaderList(statement.getDatabase(), statement.getTableName());
+    return new TableDeviceQueryScanNode(
+        queryContext.getQueryId().genPlanNodeId(),
+        database,
+        statement.getTableName(),
+        statement.getIdDeterminedPredicateList(),
+        statement.getIdFuzzyPredicate(),
+        columnHeaderList,
+        null);
+  }
 
+  private PlanNode planCountDevice(final CountDevice statement, final Analysis 
analysis) {
+    final String database = planQueryDevice(statement, analysis);
+    final List<ColumnHeader> columnHeaderList =
+        Collections.singletonList(new ColumnHeader("count(devices)", 
TSDataType.INT64));
     analysis.setRespDatasetHeader(new DatasetHeader(columnHeaderList, true));
 
-    TableDeviceQueryNode queryNode =
-        new TableDeviceQueryNode(
+    final TableDeviceQueryCountNode node =
+        new TableDeviceQueryCountNode(
             queryContext.getQueryId().genPlanNodeId(),
-            statement.getDatabase(),
+            database,
             statement.getTableName(),
             statement.getIdDeterminedPredicateList(),
             statement.getIdFuzzyPredicate(),
             columnHeaderList,
             null);
 
-    SchemaPartition schemaPartition =
+    final CountSchemaMergeNode countMergeNode =
+        new CountSchemaMergeNode(queryContext.getQueryId().genPlanNodeId());
+    countMergeNode.addChild(node);
+    return countMergeNode;
+  }
+
+  private String planQueryDevice(final AbstractQueryDevice statement, final 
Analysis analysis) {
+    queryContext.setQueryType(QueryType.READ);
+
+    final String database =
+        Objects.isNull(statement.getDatabase())
+            ? analysis.getDatabaseName()
+            : statement.getDatabase();
+
+    if (Objects.isNull(database)) {
+      throw new SemanticException("The database must be set before show 
devices.");
+    }
+
+    final SchemaPartition schemaPartition =
         statement.isIdDetermined()
-            ? metadata.getSchemaPartition(statement.getDatabase(), 
statement.getPartitionKeyList())
-            : metadata.getSchemaPartition(statement.getDatabase());
+            ? metadata.getSchemaPartition(database, 
statement.getPartitionKeyList())
+            : metadata.getSchemaPartition(database);
     analysis.setSchemaPartitionInfo(schemaPartition);
 
     if (schemaPartition.isEmpty()) {
       analysis.setFinishQueryAfterAnalyze();
     }
 
-    return queryNode;
+    if (Objects.isNull(
+        DataNodeTableCache.getInstance().getTable(database, 
statement.getTableName()))) {
+      throw new SemanticException(
+          String.format("Table '%s.%s' does not exist.", database, 
statement.getTableName()));
+    }
+    return database;
   }
 
   private List<ColumnHeader> getColumnHeaderList(String database, String 
tableName) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/AddExchangeNodes.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/AddExchangeNodes.java
index 156f8655881..90686edad5b 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/AddExchangeNodes.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/AddExchangeNodes.java
@@ -26,7 +26,8 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceSourceNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.process.ExchangeNode;
 import 
org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
@@ -73,7 +74,7 @@ public class AddExchangeNodes
 
       TRegionReplicaSet region =
           
context.nodeDistributionMap.get(rewriteNode.getPlanNodeId()).getRegion();
-      if (!region.equals(context.mostUsedDataRegion)) {
+      if (!region.equals(context.mostUsedRegion)) {
         ExchangeNode exchangeNode = new 
ExchangeNode(queryContext.getQueryId().genPlanNodeId());
         exchangeNode.addChild(rewriteNode);
         newNode.addChild(exchangeNode);
@@ -84,8 +85,7 @@ public class AddExchangeNodes
     }
 
     context.nodeDistributionMap.put(
-        node.getPlanNodeId(),
-        new NodeDistribution(SAME_WITH_SOME_CHILD, 
context.mostUsedDataRegion));
+        node.getPlanNodeId(), new NodeDistribution(SAME_WITH_SOME_CHILD, 
context.mostUsedRegion));
 
     return newNode;
   }
@@ -101,18 +101,26 @@ public class AddExchangeNodes
 
   @Override
   public PlanNode visitTableDeviceFetch(
-      TableDeviceFetchNode node, TableDistributedPlanGenerator.PlanContext 
context) {
+      final TableDeviceFetchNode node, final 
TableDistributedPlanGenerator.PlanContext context) {
+    return processTableDeviceSourceNode(node, context);
+  }
+
+  @Override
+  public PlanNode visitTableDeviceQueryScan(
+      final TableDeviceQueryScanNode node,
+      final TableDistributedPlanGenerator.PlanContext context) {
     return processTableDeviceSourceNode(node, context);
   }
 
   @Override
-  public PlanNode visitTableDeviceQuery(
-      TableDeviceQueryNode node, TableDistributedPlanGenerator.PlanContext 
context) {
+  public PlanNode visitTableDeviceQueryCount(
+      final TableDeviceQueryCountNode node,
+      final TableDistributedPlanGenerator.PlanContext context) {
     return processTableDeviceSourceNode(node, context);
   }
 
   private PlanNode processTableDeviceSourceNode(
-      TableDeviceSourceNode node, TableDistributedPlanGenerator.PlanContext 
context) {
+      final TableDeviceSourceNode node, final 
TableDistributedPlanGenerator.PlanContext context) {
     context.nodeDistributionMap.put(
         node.getPlanNodeId(),
         new NodeDistribution(SAME_WITH_ALL_CHILDREN, 
node.getRegionReplicaSet()));
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
index a264fd2e818..2db6e448e1e 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/distribute/TableDistributedPlanGenerator.java
@@ -25,8 +25,10 @@ import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
 import org.apache.iotdb.db.queryengine.plan.planner.plan.node.WritePlanNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.AbstractTableDeviceQueryNode;
 import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceFetchNode;
-import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryCountNode;
+import 
org.apache.iotdb.db.queryengine.plan.planner.plan.node.metedata.read.TableDeviceQueryScanNode;
 import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis;
 import org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry;
 import org.apache.iotdb.db.queryengine.plan.relational.planner.OrderingScheme;
@@ -372,7 +374,7 @@ public class TableDistributedPlanGenerator
         maxDeviceEntrySizeOfTableScan = 
subTableScanNode.getDeviceEntries().size();
       }
     }
-    context.mostUsedDataRegion = mostUsedDataRegion;
+    context.mostUsedRegion = mostUsedDataRegion;
 
     if (!context.hasSortProperty) {
       return resultTableScanNodeList;
@@ -484,9 +486,21 @@ public class TableDistributedPlanGenerator
 
   // ------------------- schema related interface 
---------------------------------------------
   @Override
-  public List<PlanNode> visitTableDeviceQuery(TableDeviceQueryNode node, 
PlanContext context) {
-    String database = PathUtils.qualifyDatabaseName(node.getDatabase());
-    Set<TRegionReplicaSet> schemaRegionSet = new HashSet<>();
+  public List<PlanNode> visitTableDeviceQueryScan(
+      final TableDeviceQueryScanNode node, final PlanContext context) {
+    return visitAbstractTableDeviceQuery(node, context);
+  }
+
+  @Override
+  public List<PlanNode> visitTableDeviceQueryCount(
+      final TableDeviceQueryCountNode node, final PlanContext context) {
+    return visitAbstractTableDeviceQuery(node, context);
+  }
+
+  private List<PlanNode> visitAbstractTableDeviceQuery(
+      final AbstractTableDeviceQueryNode node, final PlanContext context) {
+    final String database = PathUtils.qualifyDatabaseName(node.getDatabase());
+    final Set<TRegionReplicaSet> schemaRegionSet = new HashSet<>();
     analysis
         .getSchemaPartitionInfo()
         .getSchemaPartitionMap()
@@ -494,14 +508,15 @@ public class TableDistributedPlanGenerator
         .forEach(
             (deviceGroupId, schemaRegionReplicaSet) -> 
schemaRegionSet.add(schemaRegionReplicaSet));
 
-    context.mostUsedDataRegion = schemaRegionSet.iterator().next();
+    context.mostUsedRegion = schemaRegionSet.iterator().next();
     if (schemaRegionSet.size() == 1) {
       node.setRegionReplicaSet(schemaRegionSet.iterator().next());
       return Collections.singletonList(node);
     } else {
-      List<PlanNode> res = new ArrayList<>(schemaRegionSet.size());
-      for (TRegionReplicaSet schemaRegion : schemaRegionSet) {
-        TableDeviceQueryNode clonedChild = (TableDeviceQueryNode) node.clone();
+      final List<PlanNode> res = new ArrayList<>(schemaRegionSet.size());
+      for (final TRegionReplicaSet schemaRegion : schemaRegionSet) {
+        final AbstractTableDeviceQueryNode clonedChild =
+            (AbstractTableDeviceQueryNode) node.clone();
         clonedChild.setPlanNodeId(queryId.genPlanNodeId());
         clonedChild.setRegionReplicaSet(schemaRegion);
         res.add(clonedChild);
@@ -511,27 +526,28 @@ public class TableDistributedPlanGenerator
   }
 
   @Override
-  public List<PlanNode> visitTableDeviceFetch(TableDeviceFetchNode node, 
PlanContext context) {
-    String database = PathUtils.qualifyDatabaseName(node.getDatabase());
-    Set<TRegionReplicaSet> schemaRegionSet = new HashSet<>();
-    SchemaPartition schemaPartition = analysis.getSchemaPartitionInfo();
-    Map<TSeriesPartitionSlot, TRegionReplicaSet> databaseMap =
+  public List<PlanNode> visitTableDeviceFetch(
+      final TableDeviceFetchNode node, final PlanContext context) {
+    final String database = PathUtils.qualifyDatabaseName(node.getDatabase());
+    final Set<TRegionReplicaSet> schemaRegionSet = new HashSet<>();
+    final SchemaPartition schemaPartition = analysis.getSchemaPartitionInfo();
+    final Map<TSeriesPartitionSlot, TRegionReplicaSet> databaseMap =
         schemaPartition.getSchemaPartitionMap().get(database);
 
     databaseMap.forEach(
         (deviceGroupId, schemaRegionReplicaSet) -> 
schemaRegionSet.add(schemaRegionReplicaSet));
 
     if (schemaRegionSet.size() == 1) {
-      context.mostUsedDataRegion = schemaRegionSet.iterator().next();
-      node.setRegionReplicaSet(context.mostUsedDataRegion);
+      context.mostUsedRegion = schemaRegionSet.iterator().next();
+      node.setRegionReplicaSet(context.mostUsedRegion);
       return Collections.singletonList(node);
     } else {
-      Map<TRegionReplicaSet, TableDeviceFetchNode> tableDeviceFetchMap = new 
HashMap<>();
+      final Map<TRegionReplicaSet, TableDeviceFetchNode> tableDeviceFetchMap = 
new HashMap<>();
 
-      for (Object[] deviceIdArray : node.getDeviceIdList()) {
-        IDeviceID deviceID =
+      for (final Object[] deviceIdArray : node.getDeviceIdList()) {
+        final IDeviceID deviceID =
             
IDeviceID.Factory.DEFAULT_FACTORY.create(parseDeviceIdArray(deviceIdArray));
-        TRegionReplicaSet regionReplicaSet =
+        final TRegionReplicaSet regionReplicaSet =
             databaseMap.get(schemaPartition.calculateDeviceGroupId(deviceID));
         tableDeviceFetchMap
             .computeIfAbsent(
@@ -547,13 +563,13 @@ public class TableDistributedPlanGenerator
             .addDeviceId(deviceIdArray);
       }
 
-      List<PlanNode> res = new ArrayList<>();
+      final List<PlanNode> res = new ArrayList<>();
       TRegionReplicaSet mostUsedDataRegion = null;
       int maxDeviceEntrySizeOfTableScan = 0;
-      for (Map.Entry<TRegionReplicaSet, TableDeviceFetchNode> entry :
+      for (final Map.Entry<TRegionReplicaSet, TableDeviceFetchNode> entry :
           tableDeviceFetchMap.entrySet()) {
-        TRegionReplicaSet regionReplicaSet = entry.getKey();
-        TableDeviceFetchNode subTableDeviceFetchNode = entry.getValue();
+        final TRegionReplicaSet regionReplicaSet = entry.getKey();
+        final TableDeviceFetchNode subTableDeviceFetchNode = entry.getValue();
         res.add(subTableDeviceFetchNode);
 
         if (subTableDeviceFetchNode.getDeviceIdList().size() > 
maxDeviceEntrySizeOfTableScan) {
@@ -561,7 +577,7 @@ public class TableDistributedPlanGenerator
           maxDeviceEntrySizeOfTableScan = 
subTableDeviceFetchNode.getDeviceIdList().size();
         }
       }
-      context.mostUsedDataRegion = mostUsedDataRegion;
+      context.mostUsedRegion = mostUsedDataRegion;
       return res;
     }
   }
@@ -571,7 +587,7 @@ public class TableDistributedPlanGenerator
     boolean hasExchangeNode = false;
     boolean hasSortProperty = false;
     OrderingScheme expectedOrderingScheme;
-    TRegionReplicaSet mostUsedDataRegion;
+    TRegionReplicaSet mostUsedRegion;
 
     public PlanContext() {
       this.nodeDistributionMap = new HashMap<>();
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractQueryDevice.java
similarity index 79%
copy from 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
copy to 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractQueryDevice.java
index 3b2fee11a89..18e50a9ac55 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AbstractQueryDevice.java
@@ -21,15 +21,18 @@ package 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast;
 
 import org.apache.tsfile.file.metadata.IDeviceID;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
 
-public class ShowDevice extends Statement {
+// TODO table metadata: reuse query distinct logic
+public abstract class AbstractQueryDevice extends Statement {
 
-  private final String database;
+  private String database;
 
   private final String tableName;
 
+  // Currently unused, shall be parsed into idDeterminedPredicateList and 
idFuzzyPredicate on demand
   private Expression rawExpression;
 
   /**
@@ -49,20 +52,19 @@ public class ShowDevice extends Statement {
 
   private transient List<IDeviceID> partitionKeyList;
 
-  // for sql-input show device usage
-  public ShowDevice(String database, String tableName, Expression 
rawExpression) {
+  // For sql-input show device usage
+  protected AbstractQueryDevice(final String tableName, final Expression 
rawExpression) {
     super(null);
-    this.database = database;
     this.tableName = tableName;
     this.rawExpression = rawExpression;
   }
 
-  // for device fetch serving data query
-  public ShowDevice(
-      String database,
-      String tableName,
-      List<List<Expression>> idDeterminedPredicateList,
-      Expression idFuzzyFilterList) {
+  // For device fetch serving data query
+  protected AbstractQueryDevice(
+      final String database,
+      final String tableName,
+      final List<List<Expression>> idDeterminedPredicateList,
+      final Expression idFuzzyFilterList) {
     super(null);
     this.database = database;
     this.tableName = tableName;
@@ -84,15 +86,12 @@ public class ShowDevice extends Statement {
 
   public List<List<Expression>> getIdDeterminedPredicateList() {
     if (idDeterminedPredicateList == null) {
-      // TODO table metadata: process raw expression input by show device sql
+      idDeterminedPredicateList = 
Collections.singletonList(Collections.emptyList());
     }
     return idDeterminedPredicateList;
   }
 
   public Expression getIdFuzzyPredicate() {
-    if (idFuzzyPredicate == null) {
-      // TODO table metadata: process raw expression input by show device sql
-    }
     return idFuzzyPredicate;
   }
 
@@ -100,7 +99,7 @@ public class ShowDevice extends Statement {
     return isIdDetermined;
   }
 
-  public void setIdDetermined(boolean idDetermined) {
+  public void setIdDetermined(final boolean idDetermined) {
     isIdDetermined = idDetermined;
   }
 
@@ -111,21 +110,16 @@ public class ShowDevice extends Statement {
     return partitionKeyList;
   }
 
-  @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
-    return visitor.visitShowDevice(this, context);
-  }
-
   @Override
   public List<? extends Node> getChildren() {
     return null;
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(final Object o) {
     if (this == o) return true;
     if (o == null || getClass() != o.getClass()) return false;
-    ShowDevice that = (ShowDevice) o;
+    final AbstractQueryDevice that = (AbstractQueryDevice) o;
     return Objects.equals(database, that.database)
         && Objects.equals(tableName, that.tableName)
         && Objects.equals(rawExpression, that.rawExpression)
@@ -139,9 +133,8 @@ public class ShowDevice extends Statement {
         database, tableName, rawExpression, idDeterminedPredicateList, 
idFuzzyPredicate);
   }
 
-  @Override
-  public String toString() {
-    return "ShowDevice{"
+  protected String toStringContent() {
+    return "{"
         + "database='"
         + 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 7989c23e85d..f60360d4d06 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
@@ -494,4 +494,8 @@ public abstract class AstVisitor<R, C> {
   protected R visitShowDevice(ShowDevice node, C context) {
     return visitStatement(node, context);
   }
+
+  protected R visitCountDevice(CountDevice node, C context) {
+    return visitStatement(node, context);
+  }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDevice.java
similarity index 61%
copy from 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
copy to 
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDevice.java
index 35ec8873ba4..6cf578ec1e6 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/exception/metadata/table/TableNotExistsException.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/CountDevice.java
@@ -17,16 +17,22 @@
  * under the License.
  */
 
-package org.apache.iotdb.db.exception.metadata.table;
+package org.apache.iotdb.db.queryengine.plan.relational.sql.ast;
 
-import org.apache.iotdb.commons.exception.MetadataException;
-import org.apache.iotdb.rpc.TSStatusCode;
+public class CountDevice extends AbstractQueryDevice {
 
-public class TableNotExistsException extends MetadataException {
+  // For sql-input show device usage
+  public CountDevice(final String tableName, final Expression rawExpression) {
+    super(tableName, rawExpression);
+  }
+
+  @Override
+  public <R, C> R accept(final AstVisitor<R, C> visitor, final C context) {
+    return visitor.visitCountDevice(this, context);
+  }
 
-  public TableNotExistsException(String database, String tableName) {
-    super(
-        String.format("Table %s.%s not exists.", database, tableName),
-        TSStatusCode.TABLE_NOT_EXISTS.getStatusCode());
+  @Override
+  public String toString() {
+    return "CountDevice" + toStringContent();
   }
 }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
index 3b2fee11a89..4f8fef23ac6 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowDevice.java
@@ -19,141 +19,31 @@
 
 package org.apache.iotdb.db.queryengine.plan.relational.sql.ast;
 
-import org.apache.tsfile.file.metadata.IDeviceID;
-
 import java.util.List;
-import java.util.Objects;
-
-public class ShowDevice extends Statement {
-
-  private final String database;
-
-  private final String tableName;
-
-  private Expression rawExpression;
 
-  /**
-   * The outer list represents the OR relation between different expression 
lists.
-   *
-   * <p>The inner list represents the AND between different expression.
-   *
-   * <p>Each inner list represents a device pattern and each expression of it 
represents one
-   * condition on some id column.
-   */
-  private List<List<Expression>> idDeterminedPredicateList;
+public class ShowDevice extends AbstractQueryDevice {
 
-  /** filters/conditions involving non-id columns and concat by OR to id 
column filters */
-  private Expression idFuzzyPredicate;
-
-  private boolean isIdDetermined = false;
-
-  private transient List<IDeviceID> partitionKeyList;
-
-  // for sql-input show device usage
-  public ShowDevice(String database, String tableName, Expression 
rawExpression) {
-    super(null);
-    this.database = database;
-    this.tableName = tableName;
-    this.rawExpression = rawExpression;
+  // For sql-input show device usage
+  public ShowDevice(final String tableName, final Expression rawExpression) {
+    super(tableName, rawExpression);
   }
 
-  // for device fetch serving data query
+  // For device fetch serving data query
   public ShowDevice(
-      String database,
-      String tableName,
-      List<List<Expression>> idDeterminedPredicateList,
-      Expression idFuzzyFilterList) {
-    super(null);
-    this.database = database;
-    this.tableName = tableName;
-    this.idDeterminedPredicateList = idDeterminedPredicateList;
-    this.idFuzzyPredicate = idFuzzyFilterList;
-  }
-
-  public String getDatabase() {
-    return database;
-  }
-
-  public String getTableName() {
-    return tableName;
-  }
-
-  public Expression getRawExpression() {
-    return rawExpression;
-  }
-
-  public List<List<Expression>> getIdDeterminedPredicateList() {
-    if (idDeterminedPredicateList == null) {
-      // TODO table metadata: process raw expression input by show device sql
-    }
-    return idDeterminedPredicateList;
-  }
-
-  public Expression getIdFuzzyPredicate() {
-    if (idFuzzyPredicate == null) {
-      // TODO table metadata: process raw expression input by show device sql
-    }
-    return idFuzzyPredicate;
-  }
-
-  public boolean isIdDetermined() {
-    return isIdDetermined;
-  }
-
-  public void setIdDetermined(boolean idDetermined) {
-    isIdDetermined = idDetermined;
-  }
-
-  public List<IDeviceID> getPartitionKeyList() {
-    if (partitionKeyList == null) {
-      // TODO table metadata: parse idDeterminedFilterList to IDeviceID list
-    }
-    return partitionKeyList;
+      final String database,
+      final String tableName,
+      final List<List<Expression>> idDeterminedPredicateList,
+      final Expression idFuzzyFilterList) {
+    super(database, tableName, idDeterminedPredicateList, idFuzzyFilterList);
   }
 
   @Override
-  public <R, C> R accept(AstVisitor<R, C> visitor, C context) {
+  public <R, C> R accept(final AstVisitor<R, C> visitor, final C context) {
     return visitor.visitShowDevice(this, context);
   }
 
-  @Override
-  public List<? extends Node> getChildren() {
-    return null;
-  }
-
-  @Override
-  public boolean equals(Object o) {
-    if (this == o) return true;
-    if (o == null || getClass() != o.getClass()) return false;
-    ShowDevice that = (ShowDevice) o;
-    return Objects.equals(database, that.database)
-        && Objects.equals(tableName, that.tableName)
-        && Objects.equals(rawExpression, that.rawExpression)
-        && Objects.equals(idDeterminedPredicateList, 
that.idDeterminedPredicateList)
-        && Objects.equals(idFuzzyPredicate, that.idFuzzyPredicate);
-  }
-
-  @Override
-  public int hashCode() {
-    return Objects.hash(
-        database, tableName, rawExpression, idDeterminedPredicateList, 
idFuzzyPredicate);
-  }
-
   @Override
   public String toString() {
-    return "ShowDevice{"
-        + "database='"
-        + database
-        + '\''
-        + ", tableName='"
-        + tableName
-        + '\''
-        + ", rawExpression="
-        + rawExpression
-        + ", idDeterminedFilterList="
-        + idDeterminedPredicateList
-        + ", idFuzzyFilter="
-        + idFuzzyPredicate
-        + '}';
+    return "ShowDevice" + toStringContent();
   }
 }
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 bdbf3a3f094..472b1a433f1 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
@@ -39,6 +39,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Cast;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CoalesceExpression;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ColumnDefinition;
 import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ComparisonExpression;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CountDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateIndex;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateTable;
@@ -111,6 +112,7 @@ import 
org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCluster;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfigNodes;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDataNodes;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowIndex;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowRegions;
 import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowTables;
@@ -505,13 +507,21 @@ public class AstBuilder extends 
RelationalSqlBaseVisitor<Node> {
   }
 
   @Override
-  public Node 
visitShowDevicesStatement(RelationalSqlParser.ShowDevicesStatementContext ctx) {
-    return super.visitShowDevicesStatement(ctx);
+  public Node visitShowDevicesStatement(final 
RelationalSqlParser.ShowDevicesStatementContext ctx) {
+    if (ctx.WHERE() != null || ctx.LIMIT() != null || ctx.OFFSET() != null) {
+      throw new UnsupportedOperationException(
+          "Show devices with WHERE/LIMIT/OFFSET is unsupported yet.");
+    }
+    return new ShowDevice(getQualifiedName(ctx.tableName).toString(), null);
   }
 
   @Override
-  public Node 
visitCountDevicesStatement(RelationalSqlParser.CountDevicesStatementContext 
ctx) {
-    return super.visitCountDevicesStatement(ctx);
+  public Node visitCountDevicesStatement(
+      final RelationalSqlParser.CountDevicesStatementContext ctx) {
+    if (ctx.WHERE() != null) {
+      throw new UnsupportedOperationException("Count devices with WHERE is 
unsupported yet.");
+    }
+    return new CountDevice(getQualifiedName(ctx.tableName).toString(), null);
   }
 
   @Override
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 a10ea67456a..4c73db0e0ba 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
@@ -233,14 +233,14 @@ loadTsFileStatement
 
 // -------------------------------------------- Show Statement 
---------------------------------------------------------
 showDevicesStatement
-    : SHOW DEVICES (FROM tableName=qualifiedName)?
+    : SHOW DEVICES FROM tableName=qualifiedName
         (WHERE where=booleanExpression)?
         (OFFSET offset=rowCount (ROW | ROWS)?)?
         (LIMIT limit=limitRowCount)?
     ;
 
 countDevicesStatement
-    : COUNT DEVICES (FROM tableName=qualifiedName)? (WHERE 
where=booleanExpression)?
+    : COUNT DEVICES FROM tableName=qualifiedName (WHERE 
where=booleanExpression)?
     ;
 
 // show timeseries and count timeseries have no meaning in relational model

Reply via email to