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

Caideyipi pushed a commit to branch 1.3-mutli
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/1.3-mutli by this push:
     new 5dbdde5b70f Fixed the missing time column with non-default name & INF 
without '' when explicitly defined in show create && the bug that the db ttl is 
non-default for replacing tree views && Pipe: NPE of Deletion Sync & failed 
logic for compressing progressReportEvent (#17457)
5dbdde5b70f is described below

commit 5dbdde5b70fb8f54c18946e5fdce2b611b43eafb
Author: Caideyipi <[email protected]>
AuthorDate: Mon Apr 27 10:47:10 2026 +0800

    Fixed the missing time column with non-default name & INF without '' when 
explicitly defined in show create && the bug that the db ttl is non-default for 
replacing tree views && Pipe: NPE of Deletion Sync & failed logic for 
compressing progressReportEvent (#17457)
---
 .../relational/it/schema/IoTDBDatabaseIT.java      |  909 ++++++++++++++++
 .../iotdb/relational/it/schema/IoTDBTableIT.java   | 1117 ++++++++++++++++++++
 .../table/view/CreateTableViewProcedure.java       |  189 ++++
 .../realtime/PipeRealtimeDataRegionSource.java     |    7 +-
 .../metadata/relational/ShowCreateTableTask.java   |  157 +++
 .../metadata/relational/ShowCreateViewTask.java    |  162 +++
 .../apache/iotdb/commons/schema/table/TsTable.java |  431 ++++++++
 .../schema/table/column/TsTableColumnSchema.java   |  112 ++
 8 files changed, 3082 insertions(+), 2 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
new file mode 100644
index 00000000000..2b367b70b22
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java
@@ -0,0 +1,909 @@
+/*
+ * 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.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
+
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+import org.junit.runner.RunWith;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showDBColumnHeaders;
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showDBDetailsColumnHeaders;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBDatabaseIT {
+
+  @Before
+  public void setUp() throws Exception {
+    EnvFactory.getEnv()
+        .getConfig()
+        .getCommonConfig()
+        .setEnforceStrongPassword(false)
+        .setPipeMemoryManagementEnabled(false)
+        .setIsPipeEnableMemoryCheck(false)
+        .setPipeAutoSplitFullEnabled(false);
+    // enable subscription
+    EnvFactory.getEnv()
+        .getConfig()
+        .getCommonConfig()
+        .setPipeMemoryManagementEnabled(false)
+        .setIsPipeEnableMemoryCheck(false)
+        .setPipeAutoSplitFullEnabled(false);
+    EnvFactory.getEnv().initClusterEnvironment();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testManageDatabase() {
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+
+      // create
+      statement.execute("create database test with (ttl='INF')");
+
+      // create duplicated database without IF NOT EXISTS
+      try {
+        statement.execute("create database test");
+        fail("create database test shouldn't succeed because test already 
exists");
+      } catch (final SQLException e) {
+        assertEquals("501: Database test already exists", e.getMessage());
+      }
+
+      // create duplicated database with IF NOT EXISTS
+      statement.execute("create database IF NOT EXISTS test");
+
+      // alter non-exist
+      try {
+        statement.execute("alter database test1 set properties ttl='INF'");
+        fail("alter database test1 shouldn't succeed because test does not 
exist");
+      } catch (final SQLException e) {
+        assertEquals("500: Database test1 doesn't exist", e.getMessage());
+      }
+
+      statement.execute("alter database if exists test1 set properties 
ttl='INF'");
+      statement.execute("alter database test set properties ttl=default");
+
+      String[] databaseNames = new String[] {"test"};
+      String[] TTLs = new String[] {"INF"};
+      int[] schemaReplicaFactors = new int[] {1};
+      int[] dataReplicaFactors = new int[] {1};
+      int[] timePartitionInterval = new int[] {604800000};
+
+      // show
+      try (final ResultSet resultSet = statement.executeQuery("SHOW 
DATABASES")) {
+        int cnt = 0;
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showDBColumnHeaders.size(), metaData.getColumnCount());
+        for (int i = 0; i < showDBColumnHeaders.size(); i++) {
+          assertEquals(showDBColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          if (resultSet.getString(1).equals("information_schema")) {
+            continue;
+          }
+          assertEquals(databaseNames[cnt], resultSet.getString(1));
+          assertEquals(TTLs[cnt], resultSet.getString(2));
+          assertEquals(schemaReplicaFactors[cnt], resultSet.getInt(3));
+          assertEquals(dataReplicaFactors[cnt], resultSet.getInt(4));
+          assertEquals(timePartitionInterval[cnt], resultSet.getLong(5));
+          cnt++;
+        }
+        assertEquals(databaseNames.length, cnt);
+      }
+
+      final int[] schemaRegionGroupNum = new int[] {0};
+      final int[] dataRegionGroupNum = new int[] {0};
+      // show
+      try (final ResultSet resultSet = statement.executeQuery("SHOW DATABASES 
DETAILS")) {
+        int cnt = 0;
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showDBDetailsColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showDBDetailsColumnHeaders.size(); i++) {
+          assertEquals(
+              showDBDetailsColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          if (resultSet.getString(1).equals("information_schema")) {
+            continue;
+          }
+          assertEquals(databaseNames[cnt], resultSet.getString(1));
+          assertEquals(TTLs[cnt], resultSet.getString(2));
+          assertEquals(schemaReplicaFactors[cnt], resultSet.getInt(3));
+          assertEquals(dataReplicaFactors[cnt], resultSet.getInt(4));
+          assertEquals(timePartitionInterval[cnt], resultSet.getLong(5));
+          assertEquals(schemaRegionGroupNum[cnt], resultSet.getInt(6));
+          assertEquals(dataRegionGroupNum[cnt], resultSet.getInt(7));
+          cnt++;
+        }
+        assertEquals(databaseNames.length, cnt);
+      }
+
+      // use
+      statement.execute("use test");
+
+      // use nonexistent database
+      try {
+        statement.execute("use test1");
+        fail("use test1 shouldn't succeed because test1 doesn't exist");
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      // drop
+      statement.execute("drop database test");
+      try (final ResultSet resultSet = statement.executeQuery("SHOW 
DATABASES")) {
+        // Information_schema
+        assertTrue(resultSet.next());
+        assertFalse(resultSet.next());
+      }
+
+      // drop nonexistent database without IF EXISTS
+      try {
+        statement.execute("drop database test");
+        fail("drop database test shouldn't succeed because test doesn't 
exist");
+      } catch (final SQLException e) {
+        assertEquals("500: Database test doesn't exist", e.getMessage());
+      }
+
+      // drop nonexistent database with IF EXISTS
+      statement.execute("drop database IF EXISTS test");
+
+      // Test create database with properties
+      statement.execute(
+          "create database test_prop with (ttl=300, 
schema_region_group_num=DEFAULT, time_partition_interval=100000)");
+      databaseNames = new String[] {"test_prop"};
+      TTLs = new String[] {"300"};
+      timePartitionInterval = new int[] {100000};
+
+      // show
+      try (final ResultSet resultSet = statement.executeQuery("SHOW 
DATABASES")) {
+        int cnt = 0;
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showDBColumnHeaders.size(), metaData.getColumnCount());
+        for (int i = 0; i < showDBColumnHeaders.size(); i++) {
+          assertEquals(showDBColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          if (resultSet.getString(1).equals("information_schema")) {
+            continue;
+          }
+          assertEquals(databaseNames[cnt], resultSet.getString(1));
+          assertEquals(TTLs[cnt], resultSet.getString(2));
+          assertEquals(schemaReplicaFactors[cnt], resultSet.getInt(3));
+          assertEquals(dataReplicaFactors[cnt], resultSet.getInt(4));
+          assertEquals(timePartitionInterval[cnt], resultSet.getLong(5));
+          cnt++;
+        }
+        assertEquals(databaseNames.length, cnt);
+      }
+
+      try {
+        statement.execute("create database test_prop_2 with 
(non_exist_prop=DEFAULT)");
+        fail(
+            "create database test_prop_2 shouldn't succeed because the 
property key does not exist.");
+      } catch (final SQLException e) {
+        assertTrue(
+            e.getMessage(),
+            e.getMessage().contains("Unsupported database property key: 
non_exist_prop"));
+      }
+
+      // create with strange name
+      try {
+        statement.execute("create database 1test");
+        fail(
+            "create database 1test shouldn't succeed because 1test is not a 
legal identifier; identifiers must not start with a digit; surround the 
identifier with double quotes");
+      } catch (final SQLException e) {
+        assertTrue(e.getMessage(), e.getMessage().contains("mismatched input 
'1'"));
+      }
+
+      statement.execute("create database \"1test\"");
+      statement.execute("use \"1test\"");
+      statement.execute("drop database \"1test\"");
+
+      try {
+        statement.execute("create database 1");
+        fail("create database 1 shouldn't succeed because 1 is not a legal 
identifier");
+      } catch (final SQLException e) {
+        assertTrue(e.getMessage(), e.getMessage().contains("mismatched input 
'1'"));
+      }
+
+      statement.execute("create database \"1\"");
+      statement.execute("use \"1\"");
+      statement.execute("drop database \"1\"");
+
+      try {
+        statement.execute("create database a.b");
+        fail("create database a.b shouldn't succeed because a.b is not a legal 
identifier");
+      } catch (final SQLException e) {
+        assertTrue(e.getMessage(), e.getMessage().contains("mismatched input 
'.'"));
+      }
+
+      // Test length limitation
+      statement.execute(
+          "create database 
thisDatabaseLengthIsPreciselySixtyFourThusItCanBeNormallyCreated");
+
+      try {
+        statement.execute(
+            "create database 
thisDatabaseLengthHasExceededSixtyFourThusItCantBeNormallyCreated");
+        fail(
+            "create database 
thisDatabaseLengthHasExceededSixtyFourThusItCantBeNormallyCreated shouldn't 
succeed because it's length has exceeded 64");
+      } catch (final SQLException e) {
+        assertTrue(
+            e.getMessage(),
+            e.getMessage().contains("the length of database name shall not 
exceed 64"));
+      }
+
+    } catch (final SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void testDatabaseWithSpecificCharacters() throws SQLException {
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      try {
+        statement.execute("create database \"````x.\"");
+        fail("create database ````x. shouldn't succeed because it contains 
'.'");
+      } catch (final SQLException e) {
+        assertEquals(
+            "509: ````x. is not a legal path, because the database name can 
only contain english or chinese characters, numbers, backticks and 
underscores.",
+            e.getMessage());
+      }
+
+      try {
+        statement.execute("create database \"#\"");
+        fail("create database # shouldn't succeed because it contains illegal 
character '#'");
+      } catch (final SQLException e) {
+        assertEquals(
+            "509: # is not a legal path, because the database name can only 
contain english or chinese characters, numbers, backticks and underscores.",
+            e.getMessage());
+      }
+
+      statement.execute("create database \"````x\"");
+
+      try (final ResultSet resultSet = statement.executeQuery("SHOW 
DATABASES")) {
+        assertTrue(resultSet.next());
+        assertEquals("````x", resultSet.getString(1));
+        assertTrue(resultSet.next());
+        assertEquals("information_schema", resultSet.getString(1));
+        assertFalse(resultSet.next());
+      }
+
+      statement.execute("use \"````x\"");
+
+      statement.execute("create table table0 (a tag, b attribute, c int32)");
+
+      statement.execute("desc table0");
+      statement.execute("desc \"````x\".table0");
+
+      statement.execute("show tables");
+      statement.execute("show tables from \"````x\"");
+
+      statement.execute("insert into table0 (time, a, b, c) values(0, '1', 
'2', 3)");
+      statement.execute("insert into \"````x\".table0 (time, a, b, c) 
values(1, '1', '2', 3)");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select a, b, c from \"````x\".table0 where 
time = 0"),
+          "a,b,c,",
+          Collections.singleton("1,2,3,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from table0"),
+          "a,b,",
+          Collections.singleton("1,2,"));
+
+      statement.execute("update \"````x\".table0 set b = '4'");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from table0"),
+          "a,b,",
+          Collections.singleton("1,4,"));
+    }
+  }
+
+  @Test
+  public void testInformationSchema() throws SQLException {
+    // Use a normal user to test visibility
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("create user test 'password123456'");
+      adminStmt.execute("create pipe test ('sink'='do-nothing-sink')");
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      // Test unsupported write plans
+      final Set<String> writeSQLs =
+          new HashSet<>(
+              Arrays.asList(
+                  "create database information_schema",
+                  "drop database information_schema",
+                  // table in information_schema do not have the time column, 
add time column just
+                  // for simulate the base table
+                  "create table information_schema.tableA (time timestamp 
time)",
+                  "alter table information_schema.tableA add column a id",
+                  "alter table information_schema.tableA set properties 
ttl=default",
+                  // given that create table in information_schema is not 
allowed, skip insert
+                  // "insert into information_schema.tables (database) 
values('db')",
+                  "update information_schema.tables set status='RUNNING'"));
+
+      for (final String writeSQL : writeSQLs) {
+        try {
+          statement.execute(writeSQL);
+          fail("information_schema does not support write");
+        } catch (final SQLException e) {
+          assertEquals(
+              "701: The database 'information_schema' can only be queried", 
e.getMessage());
+        }
+      }
+
+      statement.execute("use information_schema");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show databases"),
+          
"Database,TTL(ms),SchemaReplicationFactor,DataReplicationFactor,TimePartitionInterval,",
+          Collections.singleton("information_schema,INF,null,null,null,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show tables"),
+          "TableName,TTL(ms),",
+          Arrays.asList(
+              "columns,INF,",
+              "config_nodes,INF,",
+              "configurations,INF,",
+              "connections,INF,",
+              "current_queries,INF,",
+              "data_nodes,INF,",
+              "databases,INF,",
+              "functions,INF,",
+              "keywords,INF,",
+              "nodes,INF,",
+              "pipe_plugins,INF,",
+              "pipes,INF,",
+              "queries,INF,",
+              "queries_costs_histogram,INF,",
+              "regions,INF,",
+              "services,INF,",
+              "subscriptions,INF,",
+              "table_disk_usage,INF,",
+              "tables,INF,",
+              "topics,INF,",
+              "views,INF,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc databases"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "database,STRING,TAG,",
+                  "ttl(ms),STRING,ATTRIBUTE,",
+                  "schema_replication_factor,INT32,ATTRIBUTE,",
+                  "data_replication_factor,INT32,ATTRIBUTE,",
+                  "time_partition_interval,INT64,ATTRIBUTE,",
+                  "schema_region_group_num,INT32,ATTRIBUTE,",
+                  "data_region_group_num,INT32,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc tables"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "database,STRING,TAG,",
+                  "table_name,STRING,TAG,",
+                  "ttl(ms),STRING,ATTRIBUTE,",
+                  "status,STRING,ATTRIBUTE,",
+                  "comment,STRING,ATTRIBUTE,",
+                  "table_type,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc columns"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "database,STRING,TAG,",
+                  "table_name,STRING,TAG,",
+                  "column_name,STRING,TAG,",
+                  "datatype,STRING,ATTRIBUTE,",
+                  "category,STRING,ATTRIBUTE,",
+                  "status,STRING,ATTRIBUTE,",
+                  "comment,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc queries"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "query_id,STRING,TAG,",
+                  "start_time,TIMESTAMP,ATTRIBUTE,",
+                  "datanode_id,INT32,ATTRIBUTE,",
+                  "elapsed_time,FLOAT,ATTRIBUTE,",
+                  "statement,STRING,ATTRIBUTE,",
+                  "user,STRING,ATTRIBUTE,",
+                  "wait_time_in_server,FLOAT,ATTRIBUTE,",
+                  "client_ip,STRING,ATTRIBUTE,",
+                  "timeout,INT64,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc pipes"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "id,STRING,TAG,",
+                  "creation_time,TIMESTAMP,ATTRIBUTE,",
+                  "state,STRING,ATTRIBUTE,",
+                  "pipe_source,STRING,ATTRIBUTE,",
+                  "pipe_processor,STRING,ATTRIBUTE,",
+                  "pipe_sink,STRING,ATTRIBUTE,",
+                  "exception_message,STRING,ATTRIBUTE,",
+                  "remaining_event_count,INT64,ATTRIBUTE,",
+                  "estimated_remaining_seconds,DOUBLE,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc pipe_plugins"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "plugin_name,STRING,TAG,",
+                  "plugin_type,STRING,ATTRIBUTE,",
+                  "class_name,STRING,ATTRIBUTE,",
+                  "plugin_jar,STRING,ATTRIBUTE,",
+                  "exception_message,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc topics"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList("topic_name,STRING,TAG,", 
"topic_configs,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc subscriptions"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "topic_name,STRING,TAG,",
+                  "consumer_group_name,STRING,TAG,",
+                  "subscribed_consumers,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc services"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "service_name,STRING,TAG,",
+                  "datanode_id,INT32,ATTRIBUTE,",
+                  "state,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc views"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "database,STRING,TAG,",
+                  "table_name,STRING,TAG,",
+                  "view_definition,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc functions"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "function_table,STRING,TAG,",
+                  "function_type,STRING,ATTRIBUTE,",
+                  "class_name(udf),STRING,ATTRIBUTE,",
+                  "state,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc configurations"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(Arrays.asList("variable,STRING,TAG,", 
"value,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc keywords"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(Arrays.asList("word,STRING,TAG,", 
"reserved,INT32,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc nodes"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "node_id,INT32,TAG,",
+                  "node_type,STRING,ATTRIBUTE,",
+                  "status,STRING,ATTRIBUTE,",
+                  "internal_address,STRING,ATTRIBUTE,",
+                  "internal_port,INT32,ATTRIBUTE,",
+                  "version,STRING,ATTRIBUTE,",
+                  "build_info,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc config_nodes"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "node_id,INT32,TAG,",
+                  "config_consensus_port,INT32,ATTRIBUTE,",
+                  "role,STRING,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc data_nodes"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "node_id,INT32,TAG,",
+                  "data_region_num,INT32,ATTRIBUTE,",
+                  "schema_region_num,INT32,ATTRIBUTE,",
+                  "rpc_address,STRING,ATTRIBUTE,",
+                  "rpc_port,INT32,ATTRIBUTE,",
+                  "mpp_port,INT32,ATTRIBUTE,",
+                  "data_consensus_port,INT32,ATTRIBUTE,",
+                  "schema_consensus_port,INT32,ATTRIBUTE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc table_disk_usage"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "database,STRING,FIELD,",
+                  "table_name,STRING,FIELD,",
+                  "datanode_id,INT32,FIELD,",
+                  "region_id,INT32,FIELD,",
+                  "time_partition,INT64,FIELD,",
+                  "size_in_bytes,INT64,FIELD,")));
+
+      // Only root user is allowed
+      Assert.assertThrows(SQLException.class, () -> statement.execute("select 
* from regions"));
+      Assert.assertThrows(SQLException.class, () -> statement.execute("select 
* from topics"));
+      Assert.assertThrows(
+          SQLException.class, () -> statement.execute("select * from 
subscriptions"));
+      Assert.assertThrows(
+          SQLException.class, () -> statement.execute("select * from 
configurations"));
+      Assert.assertThrows(SQLException.class, () -> statement.execute("select 
* from nodes"));
+      Assert.assertThrows(
+          SQLException.class, () -> statement.execute("select * from 
config_nodes"));
+      Assert.assertThrows(SQLException.class, () -> statement.execute("select 
* from data_nodes"));
+      Assert.assertThrows(
+          SQLException.class, () -> statement.executeQuery("select * from 
pipe_plugins"));
+      Assert.assertThrows(
+          SQLException.class, () -> statement.executeQuery("select * from 
table_disk_usage"));
+
+      // Filter out not self-created pipes
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select * from pipes"),
+          
"id,creation_time,state,pipe_source,pipe_processor,pipe_sink,exception_message,remaining_event_count,estimated_remaining_seconds,",
+          Collections.emptySet());
+
+      // No auth needed
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select distinct(function_type) from 
information_schema.functions"),
+          "function_type,",
+          new HashSet<>(
+              Arrays.asList(
+                  "built-in scalar function,",
+                  "built-in aggregate function,",
+                  "built-in table function,")));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select * from information_schema.keywords where reserved > 0 
limit 1"),
+          "word,reserved,",
+          Collections.singleton("ACCOUNT,1,"));
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      // Test table query
+      statement.execute("use information_schema");
+
+      statement.execute("create database test");
+      statement.execute(
+          "create table test.test (a tag, b attribute, c int32 comment 
'turbine') comment 'test'");
+      statement.execute(
+          "CREATE VIEW test.view_table (tag1 STRING TAG,tag2 STRING TAG,s11 
INT32 FIELD,s3 INT32 FIELD FROM s2) RESTRICT WITH (ttl=100) AS root.\"a\".**");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select * from databases"),
+          
"database,ttl(ms),schema_replication_factor,data_replication_factor,time_partition_interval,schema_region_group_num,data_region_group_num,",
+          new HashSet<>(
+              Arrays.asList(
+                  "information_schema,INF,null,null,null,null,null,",
+                  "test,INF,1,1,604800000,0,0,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from tables where status = 
'USING'"),
+          "database,table_name,ttl(ms),status,comment,table_type,",
+          new HashSet<>(
+              Arrays.asList(
+                  "information_schema,databases,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,tables,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,columns,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,queries,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,regions,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,topics,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,pipe_plugins,INF,USING,null,SYSTEM 
VIEW,",
+                  "information_schema,pipes,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,services,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,subscriptions,INF,USING,null,SYSTEM 
VIEW,",
+                  "information_schema,views,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,functions,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,configurations,INF,USING,null,SYSTEM 
VIEW,",
+                  "information_schema,keywords,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,nodes,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,table_disk_usage,INF,USING,null,SYSTEM 
VIEW,",
+                  "information_schema,config_nodes,INF,USING,null,SYSTEM 
VIEW,",
+                  "information_schema,data_nodes,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,connections,INF,USING,null,SYSTEM VIEW,",
+                  "information_schema,current_queries,INF,USING,null,SYSTEM 
VIEW,",
+                  
"information_schema,queries_costs_histogram,INF,USING,null,SYSTEM VIEW,",
+                  "test,test,INF,USING,test,BASE TABLE,",
+                  "test,view_table,100,USING,null,VIEW FROM TREE,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("count devices from tables where status = 
'USING'"),
+          "count(devices),",
+          Collections.singleton("23,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select * from columns where table_name = 'queries' or database 
= 'test'"),
+          "database,table_name,column_name,datatype,category,status,comment,",
+          new HashSet<>(
+              Arrays.asList(
+                  "information_schema,queries,query_id,STRING,TAG,USING,null,",
+                  
"information_schema,queries,start_time,TIMESTAMP,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,datanode_id,INT32,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,elapsed_time,FLOAT,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,statement,STRING,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,user,STRING,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,wait_time_in_server,FLOAT,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,client_ip,STRING,ATTRIBUTE,USING,null,",
+                  
"information_schema,queries,timeout,INT64,ATTRIBUTE,USING,null,",
+                  "test,test,time,TIMESTAMP,TIME,USING,null,",
+                  "test,test,a,STRING,TAG,USING,null,",
+                  "test,test,b,STRING,ATTRIBUTE,USING,null,",
+                  "test,test,c,INT32,FIELD,USING,turbine,",
+                  "test,view_table,time,TIMESTAMP,TIME,USING,null,",
+                  "test,view_table,tag1,STRING,TAG,USING,null,",
+                  "test,view_table,tag2,STRING,TAG,USING,null,",
+                  "test,view_table,s11,INT32,FIELD,USING,null,",
+                  "test,view_table,s3,INT32,FIELD,USING,null,")));
+
+      statement.execute(
+          "create pipe a2b with source('double-living'='true') with sink 
('sink'='write-back-sink')");
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select id from pipes where creation_time > 
0"),
+          "id,",
+          new HashSet<>(Arrays.asList("a2b,", "test,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select * from pipe_plugins where plugin_name = 
'IOTDB-THRIFT-SINK'"),
+          "plugin_name,plugin_type,class_name,plugin_jar,exception_message,",
+          Collections.singleton(
+              
"IOTDB-THRIFT-SINK,Builtin,org.apache.iotdb.commons.pipe.agent.plugin.builtin.sink.iotdb.thrift.IoTDBThriftSink,null,null,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select * from views"),
+          "database,table_name,view_definition,",
+          Collections.singleton(
+              "test,view_table,CREATE VIEW \"view_table\" (\"time\" TIMESTAMP 
TIME,\"tag1\" STRING TAG,\"tag2\" STRING TAG,\"s11\" INT32 FIELD,\"s3\" INT32 
FIELD FROM \"s2\") RESTRICT WITH (ttl=100) AS root.\"a\".**,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select distinct(function_type) from 
information_schema.functions"),
+          "function_type,",
+          new HashSet<>(
+              Arrays.asList(
+                  "built-in scalar function,",
+                  "built-in aggregate function,",
+                  "built-in table function,")));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select value from information_schema.configurations where 
variable = 'TimestampPrecision'"),
+          "value,",
+          Collections.singleton("ms,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery(
+              "select * from information_schema.keywords where reserved > 0 
limit 1"),
+          "word,reserved,",
+          Collections.singleton("ACCOUNT,1,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select distinct(status) from 
information_schema.nodes"),
+          "status,",
+          Collections.singleton("Running,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select count(*) from 
information_schema.config_nodes"),
+          "_col0,",
+          
Collections.singleton(EnvFactory.getEnv().getConfigNodeWrapperList().size() + 
","));
+
+      Set<String> resultSet = new HashSet<>();
+      resultSet.add("0,");
+      for (int i = 1; i < EnvFactory.getEnv().getDataNodeWrapperList().size(); 
i++) {
+        resultSet.add("0,");
+      }
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select data_region_num from 
information_schema.data_nodes"),
+          "data_region_num,",
+          resultSet);
+    }
+  }
+
+  @Test
+  public void testMixedDatabase() 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(id1 tag, s1 string)");
+      statement.execute("insert into table1 values(0, 'd1', null), (1,'d1', 
1)");
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database root.test");
+      statement.execute(
+          "alter database root.test WITH SCHEMA_REGION_GROUP_NUM=2, 
DATA_REGION_GROUP_NUM=3");
+      statement.execute("insert into root.test.d1 (s1) values(1)");
+      statement.execute("drop database root.test");
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("use test");
+      // Avoid clearing table cache
+      statement.execute("select * from table1");
+
+      try (final ResultSet resultSet = statement.executeQuery("SHOW DATABASES 
DETAILS")) {
+        assertTrue(resultSet.next());
+        assertEquals("information_schema", resultSet.getString(1));
+        assertTrue(resultSet.next());
+        assertEquals("test", resultSet.getString(1));
+        assertFalse(resultSet.next());
+      }
+
+      // Test adjustMaxRegionGroupNum
+      statement.execute(
+          "create table table2(region_id STRING TAG, plant_id STRING TAG, 
color STRING ATTRIBUTE, temperature FLOAT FIELD, speed DOUBLE FIELD)");
+      statement.execute(
+          "insert into table2(region_id, plant_id, color, temperature, speed) 
values(1, 1, 1, 1, 1)");
+
+      statement.execute("create database test1");
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database root.test");
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("drop database test");
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      // One for AUDIT database
+      TestUtils.assertResultSetSize(statement.executeQuery("show databases"), 
1);
+    }
+  }
+
+  @Test
+  public void testDBAuth() throws SQLException {
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("create user test 'password123456'");
+      adminStmt.execute("create database db");
+      adminStmt.execute(
+          "create pipe a2b with source('double-living'='true') with sink 
('sink'='write-back-sink')");
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      TestUtils.assertResultSetEqual(
+          userStmt.executeQuery("show databases"),
+          
"Database,TTL(ms),SchemaReplicationFactor,DataReplicationFactor,TimePartitionInterval,",
+          Collections.singleton("information_schema,INF,null,null,null,"));
+      TestUtils.assertResultSetEqual(
+          userStmt.executeQuery("select * from information_schema.databases"),
+          
"database,ttl(ms),schema_replication_factor,data_replication_factor,time_partition_interval,schema_region_group_num,data_region_group_num,",
+          
Collections.singleton("information_schema,INF,null,null,null,null,null,"));
+    }
+
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("GRANT SELECT ON DATABASE DB to user test");
+
+      // Information_schema does not support grant & revoke
+      Assert.assertThrows(
+          SQLException.class,
+          () -> {
+            adminStmt.execute("GRANT SELECT ON DATABASE information_schema to 
user test");
+          });
+
+      Assert.assertThrows(
+          SQLException.class,
+          () -> {
+            adminStmt.execute("REVOKE SELECT ON information_schema.tables from 
user test");
+          });
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      try (final ResultSet resultSet = userStmt.executeQuery("SHOW 
DATABASES")) {
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showDBColumnHeaders.size(), metaData.getColumnCount());
+        for (int i = 0; i < showDBColumnHeaders.size(); i++) {
+          assertEquals(showDBColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        Assert.assertTrue(resultSet.next());
+        assertEquals("db", resultSet.getString(1));
+        Assert.assertTrue(resultSet.next());
+        assertEquals("information_schema", resultSet.getString(1));
+        Assert.assertFalse(resultSet.next());
+      }
+
+      Assert.assertThrows(
+          SQLException.class,
+          () -> {
+            userStmt.execute("alter database db set properties ttl=6600000");
+          });
+
+      Assert.assertThrows(
+          SQLException.class,
+          () -> {
+            userStmt.execute("drop database db");
+          });
+    }
+
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("GRANT DROP ON ANY to user test");
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      userStmt.execute("drop database db");
+    }
+  }
+}
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
new file mode 100644
index 00000000000..c3a72d3c23a
--- /dev/null
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBTableIT.java
@@ -0,0 +1,1117 @@
+/*
+ * 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.isession.ITableSession;
+import org.apache.iotdb.it.env.EnvFactory;
+import org.apache.iotdb.it.framework.IoTDBTestRunner;
+import org.apache.iotdb.itbase.category.TableClusterIT;
+import org.apache.iotdb.itbase.category.TableLocalStandaloneIT;
+import org.apache.iotdb.itbase.env.BaseEnv;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+
+import org.apache.tsfile.enums.ColumnCategory;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.write.record.Tablet;
+import org.apache.tsfile.write.schema.IMeasurementSchema;
+import org.apache.tsfile.write.schema.MeasurementSchema;
+import org.junit.AfterClass;
+import org.junit.Assert;
+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.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.describeTableColumnHeaders;
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.describeTableDetailsColumnHeaders;
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showDBColumnHeaders;
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showTablesColumnHeaders;
+import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.showTablesDetailsColumnHeaders;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(IoTDBTestRunner.class)
+@Category({TableLocalStandaloneIT.class, TableClusterIT.class})
+public class IoTDBTableIT {
+
+  @BeforeClass
+  public static void setUp() throws Exception {
+    
EnvFactory.getEnv().getConfig().getCommonConfig().setEnforceStrongPassword(false);
+    
EnvFactory.getEnv().getConfig().getCommonConfig().setRestrictObjectLimit(true);
+    EnvFactory.getEnv().initClusterEnvironment();
+  }
+
+  @AfterClass
+  public static void tearDown() throws Exception {
+    EnvFactory.getEnv().cleanClusterEnvironment();
+  }
+
+  @Test
+  public void testManageTable() {
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+
+      statement.execute("create database test1");
+      statement.execute("create database test2 with (ttl=3000000)");
+
+      // should specify database before create table
+      try {
+        statement.execute(
+            "create table table1(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("701: database is not specified", e.getMessage());
+      }
+
+      // Show tables shall succeed in a newly created database with no tables
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables 
from test1")) {
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        assertFalse(resultSet.next());
+      }
+
+      try {
+        statement.execute(
+            "create table test1.table1(region_id STRING TAG, plant_id STRING 
TAG, device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, 
humidity DOUBLE FIELD) with (ttl=100, ttl=200, ttl=300)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("701: Duplicated property: ttl", e.getMessage());
+      }
+
+      // or use full qualified table name
+      // test "TTL=INF"
+      // "FIELD" can be omitted when type is specified
+      // "STRING" can be omitted when tag/attribute is specified
+      statement.execute(
+          "create table test1.table1(time TIMESTAMP TIME COMMENT 
'column_comment', region_id STRING TAG, plant_id STRING TAG, device_id TAG, 
model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity DOUBLE) comment 
'test' with (TTL='INF')");
+
+      try {
+        statement.execute(
+            "create table test1.table1(region_id STRING TAG, plant_id STRING 
TAG, device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, 
humidity DOUBLE FIELD)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("551: Table 'test1.table1' already exists.", 
e.getMessage());
+      }
+
+      String[] tableNames = new String[] {"table1"};
+      String[] ttls = new String[] {"INF"};
+      String[] statuses = new String[] {"USING"};
+      String[] comments = new String[] {"test"};
+
+      statement.execute("use test2");
+
+      // show tables by specifying another database
+      // Check duplicate create table won't affect table state
+      // using SHOW tables in
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables 
details in test1")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesDetailsColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesDetailsColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesDetailsColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(tableNames[cnt], resultSet.getString(1));
+          assertEquals(ttls[cnt], resultSet.getString(2));
+          assertEquals(statuses[cnt], resultSet.getString(3));
+          assertEquals(comments[cnt], resultSet.getString(4));
+          cnt++;
+        }
+        assertEquals(tableNames.length, cnt);
+      }
+
+      // Test unsupported, to be deleted
+      try {
+        statement.execute("alter table test1.table1 rename to tableN");
+      } catch (final SQLException e) {
+        assertEquals("701: The renaming for base table is currently 
unsupported", e.getMessage());
+      }
+
+      // Test unsupported, to be deleted
+      try {
+        statement.execute(
+            "alter table if exists test_db.table1 rename column if exists 
model to modelType");
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: The renaming for base table column is currently 
unsupported", e.getMessage());
+      }
+
+      // Alter table properties
+      statement.execute("alter table test1.table1 set properties ttl=1000000");
+      ttls = new String[] {"1000000"};
+
+      // Alter non-exist table
+      try {
+        statement.execute("alter table test1.nonExist set properties 
ttl=1000000");
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'test1.nonexist' does not exist", 
e.getMessage());
+      }
+
+      // If exists
+      statement.execute("alter table if exists test1.nonExist set properties 
ttl=1000000");
+
+      // Alter non-supported properties
+      try {
+        statement.execute("alter table test1.table1 set properties 
nonSupport=1000000");
+      } catch (final SQLException e) {
+        assertEquals("701: Table property 'nonsupport' is currently not 
allowed.", e.getMessage());
+      }
+
+      statement.execute("comment on table test1.table1 is 'new_test'");
+      comments = new String[] {"new_test"};
+      // using SHOW tables from
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables 
details from test1")) {
+        int cnt = 0;
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesDetailsColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesDetailsColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesDetailsColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(tableNames[cnt], resultSet.getString(1));
+          assertEquals(ttls[cnt], resultSet.getString(2));
+          assertEquals(comments[cnt], resultSet.getString(4));
+          cnt++;
+        }
+        assertEquals(tableNames.length, cnt);
+      }
+
+      // Set back to default
+      statement.execute("alter table test1.table1 set properties ttl=DEFAULT");
+      ttls = new String[] {"INF"};
+
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables 
from test1")) {
+        int cnt = 0;
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(tableNames[cnt], resultSet.getString(1));
+          assertEquals(ttls[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(tableNames.length, cnt);
+      }
+
+      // Create if not exist
+      statement.execute(
+          "create table if not exists test1.table1(region_id STRING TAG, 
plant_id STRING TAG, device_id STRING TAG, model STRING ATTRIBUTE, temperature 
FLOAT FIELD, humidity DOUBLE FIELD)");
+
+      try {
+        statement.execute(
+            "create table table2(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (UNKNOWN=3600000)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("701: Table property 'unknown' is currently not 
allowed.", e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=null)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: ttl value must be a LongLiteral, but now is NullLiteral, 
value: null",
+            e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=-1)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: ttl value must be equal to or greater than 0, but now is: 
-1", e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id TEXT TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=3600000)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: DataType of TAG Column should only be STRING, current is 
TEXT", e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id INT32 TAG, plant_id STRING TAG, 
device_id STRING TAG, model STRING ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=3600000)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: DataType of TAG Column should only be STRING, current is 
INT32", e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model TEXT ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=3600000)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: DataType of ATTRIBUTE Column should only be STRING, current 
is TEXT",
+            e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create table table2(region_id STRING TAG, plant_id STRING TAG, 
device_id STRING TAG, model DOUBLE ATTRIBUTE, temperature FLOAT FIELD, humidity 
DOUBLE FIELD) with (TTL=3600000)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: DataType of ATTRIBUTE Column should only be STRING, current 
is DOUBLE",
+            e.getMessage());
+      }
+
+      statement.execute(
+          "create table table2(t1 TIMESTAMP TIME, region_id STRING TAG, 
plant_id STRING TAG, color STRING ATTRIBUTE, temperature FLOAT FIELD) with 
(TTL=6600000)");
+
+      statement.execute("alter table table2 add column speed DOUBLE FIELD 
COMMENT 'fast'");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show create table table2"),
+          "Table,Create Table,",
+          Collections.singleton(
+              "table2,CREATE TABLE \"table2\" (\"t1\" TIMESTAMP 
TIME,\"region_id\" STRING TAG,\"plant_id\" STRING TAG,\"color\" STRING 
ATTRIBUTE,\"temperature\" FLOAT FIELD,\"speed\" DOUBLE FIELD COMMENT 'fast') 
WITH (ttl=6600000),"));
+
+      try {
+        statement.execute("alter table table2 add column speed DOUBLE FIELD");
+      } catch (final SQLException e) {
+        assertEquals("552: Column 'speed' already exist", e.getMessage());
+      }
+
+      statement.execute("alter table table2 add column if not exists speed 
DOUBLE FIELD");
+
+      try {
+        statement.execute("alter table table3 add column speed DOUBLE FIELD");
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'test2.table3' does not exist", 
e.getMessage());
+      }
+
+      statement.execute("alter table if exists table3 add column speed DOUBLE 
FIELD");
+
+      // Test create table with only time column
+      statement.execute("create table table3()");
+
+      tableNames = new String[] {"table2", "table3"};
+      ttls = new String[] {"6600000", "3000000"};
+
+      // show tables from current database
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(tableNames[cnt], resultSet.getString(1));
+          assertEquals(ttls[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(tableNames.length, cnt);
+      }
+
+      // Will not affect the manual "6600000"
+      statement.execute("alter database test2 set properties ttl=6600000");
+      statement.execute("alter database test2 set properties ttl=DEFAULT");
+
+      statement.execute("alter table table3 set properties ttl=1000000");
+      statement.execute("alter table table3 set properties ttl=DEFAULT");
+
+      ttls = new String[] {"6600000", "INF"};
+      // The table3's ttl shall be "INF"
+      try (final ResultSet resultSet = statement.executeQuery("SHOW tables")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showTablesColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < showTablesColumnHeaders.size(); i++) {
+          assertEquals(
+              showTablesColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(tableNames[cnt], resultSet.getString(1));
+          assertEquals(ttls[cnt], resultSet.getString(2));
+          cnt++;
+        }
+        assertEquals(tableNames.length, cnt);
+      }
+
+      // show tables from a non-exist database
+      try {
+        statement.executeQuery("SHOW tables from test3");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test3", e.getMessage());
+      }
+
+      // describe
+      try {
+        statement.executeQuery("describe table1");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'test2.table1' does not exist.", 
e.getMessage());
+      }
+
+      String[] columnNames =
+          new String[] {
+            "time", "region_id", "plant_id", "device_id", "model", 
"temperature", "humidity"
+          };
+      String[] dataTypes =
+          new String[] {"TIMESTAMP", "STRING", "STRING", "STRING", "STRING", 
"FLOAT", "DOUBLE"};
+      String[] categories =
+          new String[] {"TIME", "TAG", "TAG", "TAG", "ATTRIBUTE", "FIELD", 
"FIELD"};
+
+      try (final ResultSet resultSet = statement.executeQuery("describe 
test1.table1")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeTableColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeTableColumnHeaders.size(); i++) {
+          assertEquals(
+              describeTableColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          assertEquals(categories[cnt], resultSet.getString(3));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      columnNames = new String[] {"t1", "region_id", "plant_id", "color", 
"temperature", "speed"};
+      dataTypes = new String[] {"TIMESTAMP", "STRING", "STRING", "STRING", 
"FLOAT", "DOUBLE"};
+      categories = new String[] {"TIME", "TAG", "TAG", "ATTRIBUTE", "FIELD", 
"FIELD"};
+
+      try (final ResultSet resultSet = statement.executeQuery("desc table2")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeTableColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeTableColumnHeaders.size(); i++) {
+          assertEquals(
+              describeTableColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          assertEquals(categories[cnt], resultSet.getString(3));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      statement.execute(
+          "insert into table2(region_id, plant_id, color, temperature, speed) 
values(1, 1, 1, 1, 1)");
+
+      // Test drop column
+      statement.execute("alter table table2 drop column color");
+
+      // Test comment
+      // Before
+      columnNames = new String[] {"t1", "region_id", "plant_id", 
"temperature", "speed"};
+      dataTypes = new String[] {"TIMESTAMP", "STRING", "STRING", "FLOAT", 
"DOUBLE"};
+      categories = new String[] {"TIME", "TAG", "TAG", "FIELD", "FIELD"};
+      statuses = new String[] {"USING", "USING", "USING", "USING", "USING"};
+
+      comments = new String[] {null, null, null, null, "fast"};
+      try (final ResultSet resultSet = statement.executeQuery("describe table2 
details")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeTableDetailsColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeTableDetailsColumnHeaders.size(); i++) {
+          assertEquals(
+              describeTableDetailsColumnHeaders.get(i).getColumnName(),
+              metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          assertEquals(categories[cnt], resultSet.getString(3));
+          assertEquals(statuses[cnt], resultSet.getString(4));
+          assertEquals(comments[cnt], resultSet.getString(5));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      // After
+      statement.execute("COMMENT ON COLUMN table2.region_id IS '重庆'");
+      statement.execute("COMMENT ON COLUMN table2.region_id IS NULL");
+      statement.execute("COMMENT ON COLUMN test2.table2.t1 IS 'recent'");
+      statement.execute("COMMENT ON COLUMN test2.table2.region_id IS ''");
+
+      comments = new String[] {"recent", "", null, null, "fast"};
+      try (final ResultSet resultSet = statement.executeQuery("describe table2 
details")) {
+        int cnt = 0;
+        ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(describeTableDetailsColumnHeaders.size(), 
metaData.getColumnCount());
+        for (int i = 0; i < describeTableDetailsColumnHeaders.size(); i++) {
+          assertEquals(
+              describeTableDetailsColumnHeaders.get(i).getColumnName(),
+              metaData.getColumnName(i + 1));
+        }
+        while (resultSet.next()) {
+          assertEquals(columnNames[cnt], resultSet.getString(1));
+          assertEquals(dataTypes[cnt], resultSet.getString(2));
+          assertEquals(categories[cnt], resultSet.getString(3));
+          assertEquals(statuses[cnt], resultSet.getString(4));
+          assertEquals(comments[cnt], resultSet.getString(5));
+          cnt++;
+        }
+        assertEquals(columnNames.length, cnt);
+      }
+
+      statement.execute("alter table table2 drop column speed");
+
+      try {
+        statement.executeQuery("select color from table2");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("616: Column 'color' cannot be resolved", e.getMessage());
+      }
+
+      try {
+        statement.executeQuery("select speed from table2");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("616: Column 'speed' cannot be resolved", e.getMessage());
+      }
+
+      try {
+        statement.execute("alter table table2 drop column speed");
+      } catch (final SQLException e) {
+        assertEquals("616: Column speed in table 'test2.table2' does not 
exist.", e.getMessage());
+      }
+
+      try {
+        statement.execute("alter table table2 drop column t1");
+      } catch (final SQLException e) {
+        assertEquals("701: Dropping tag or time column is not supported.", 
e.getMessage());
+      }
+
+      // test data deletion by drop column
+      statement.execute("alter table table2 add column speed double");
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("select speed from table2"),
+          "speed,",
+          Collections.singleton("null,"));
+
+      statement.execute("drop table table2");
+      try {
+        statement.executeQuery("describe table2");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'test2.table2' does not exist.", 
e.getMessage());
+      }
+      statement.execute(
+          "create table table2(region_id STRING TAG, plant_id STRING TAG, 
color STRING ATTRIBUTE, temperature FLOAT FIELD, speed DOUBLE FIELD)");
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("count devices from table2"),
+          "count(devices),",
+          Collections.singleton("0,"));
+
+      // Test data deletion by drop table
+      statement.execute(
+          "insert into table2(region_id, plant_id, color, temperature, speed) 
values(1, 1, 1, 1, 1)");
+      TestUtils.assertResultSetSize(statement.executeQuery("select * from 
table2"), 1);
+
+      try {
+        statement.executeQuery("describe test3.table3");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test3", e.getMessage());
+      }
+
+      statement.execute("drop database test1");
+
+      // Test error messages
+      try {
+        statement.executeQuery("SHOW tables from test1");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("create table test1.test()");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("alter table test1.test add column a int32");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("alter table test1.test drop column a");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("alter table test1.test set properties ttl=default");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("desc test1.test");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      try {
+        statement.execute("drop table test1.test");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("500: Unknown database test1", e.getMessage());
+      }
+
+      // Test time column
+      // More time column tests are included in other IT
+      statement.execute("create table test100 (t1 time) with (ttl='INF')");
+      statement.execute("create table test101 (t1 timestamp time)");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show create table test100"),
+          "Table,Create Table,",
+          Collections.singleton(
+              "test100,CREATE TABLE \"test100\" (\"t1\" TIMESTAMP TIME) WITH 
(ttl='INF'),"));
+    } catch (final SQLException e) {
+      e.printStackTrace();
+      fail(e.getMessage());
+    }
+  }
+
+  @Test
+  public void testTableAuth() throws SQLException {
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("create user test 'password123456'");
+      adminStmt.execute("create database db");
+      adminStmt.execute("use db");
+      adminStmt.execute("create table test (a tag, b attribute, c int32)");
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      Assert.assertThrows(SQLException.class, () -> userStmt.execute("select * 
from db.test"));
+      TestUtils.assertResultSetEqual(
+          userStmt.executeQuery("select * from information_schema.tables where 
database = 'db'"),
+          "database,table_name,ttl(ms),status,comment,table_type,",
+          Collections.emptySet());
+      TestUtils.assertResultSetEqual(
+          userStmt.executeQuery("select * from information_schema.columns 
where database = 'db'"),
+          "database,table_name,column_name,datatype,category,status,comment,",
+          Collections.emptySet());
+    }
+
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("GRANT SELECT ON db.test to user test");
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      try (final ResultSet resultSet = userStmt.executeQuery("SHOW 
DATABASES")) {
+        final ResultSetMetaData metaData = resultSet.getMetaData();
+        assertEquals(showDBColumnHeaders.size(), metaData.getColumnCount());
+        for (int i = 0; i < showDBColumnHeaders.size(); i++) {
+          assertEquals(showDBColumnHeaders.get(i).getColumnName(), 
metaData.getColumnName(i + 1));
+        }
+        Assert.assertTrue(resultSet.next());
+        assertEquals("db", resultSet.getString(1));
+        Assert.assertTrue(resultSet.next());
+        assertEquals("information_schema", resultSet.getString(1));
+        Assert.assertFalse(resultSet.next());
+      }
+
+      userStmt.execute("select * from db.test");
+    }
+
+    try (final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("GRANT DROP ON DATABASE DB to user test");
+    }
+
+    try (final Connection userCon =
+            EnvFactory.getEnv().getConnection("test", "password123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement userStmt = userCon.createStatement()) {
+      userStmt.execute("use db");
+      userStmt.execute("drop table test");
+    }
+  }
+
+  // Test deadlock
+  @Test(timeout = 60000)
+  public void testConcurrentAutoCreateAndDropColumn() throws Exception {
+    try (final ITableSession session = 
EnvFactory.getEnv().getTableSessionConnection();
+        final Connection adminCon = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement adminStmt = adminCon.createStatement()) {
+      adminStmt.execute("create database db1");
+      session.executeNonQueryStatement("USE \"db1\"");
+
+      final StringBuilder sb = new StringBuilder("CREATE TABLE table8 (tag1 
string tag");
+      for (int i = 0; i < 100; ++i) {
+        sb.append(String.format(", m%s string", i));
+      }
+      sb.append(")");
+      session.executeNonQueryStatement(sb.toString());
+
+      final Thread insertThread =
+          new Thread(
+              () -> {
+                for (int i = 0; i < 100; ++i) {
+                  final List<IMeasurementSchema> schemaList = new 
ArrayList<>();
+                  schemaList.add(new MeasurementSchema("tag1", 
TSDataType.STRING));
+                  schemaList.add(new MeasurementSchema("attr1", 
TSDataType.STRING));
+                  schemaList.add(
+                      new MeasurementSchema(String.format("m%s", 100 + i), 
TSDataType.DOUBLE));
+                  final List<ColumnCategory> columnTypes =
+                      Arrays.asList(
+                          ColumnCategory.TAG, ColumnCategory.ATTRIBUTE, 
ColumnCategory.FIELD);
+
+                  long timestamp = 0;
+
+                  final Tablet tablet =
+                      new Tablet(
+                          "table8",
+                          
IMeasurementSchema.getMeasurementNameList(schemaList),
+                          IMeasurementSchema.getDataTypeList(schemaList),
+                          columnTypes,
+                          15);
+
+                  for (int row = 0; row < 15; row++) {
+                    tablet.addTimestamp(row, timestamp);
+                    tablet.addValue("tag1", row, "tag:" + timestamp);
+                    tablet.addValue("attr1", row, "attr:" + timestamp);
+                    tablet.addValue(String.format("m%s", 100 + i), row, 
timestamp * 1.0);
+                    timestamp++;
+                  }
+
+                  try {
+                    session.insert(tablet);
+                  } catch (final StatementExecutionException | 
IoTDBConnectionException e) {
+                    throw new RuntimeException(e);
+                  }
+                  tablet.reset();
+                }
+              });
+
+      final Thread deletionThread =
+          new Thread(
+              () -> {
+                for (int i = 0; i < 100; ++i) {
+                  try {
+                    adminStmt.execute(String.format("alter table db1.table8 
drop column m%s", i));
+                  } catch (final SQLException e) {
+                    throw new RuntimeException(e);
+                  }
+                }
+              });
+
+      insertThread.start();
+      deletionThread.start();
+
+      insertThread.join();
+      deletionThread.join();
+    }
+  }
+
+  @Test
+  public void testTreeViewTable() throws Exception {
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database root.another");
+      statement.execute("create database root.`重庆`.`1`.b");
+      statement.execute("create timeSeries root.`重庆`.`1`.b.`2`.S1 int32");
+      statement.execute("create timeSeries root.`重庆`.`1`.b.`2`.s2 string");
+      statement.execute("create timeSeries root.`重庆`.`1`.b.S1 int32");
+    } catch (SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create database tree_view_db");
+      statement.execute("use tree_view_db");
+
+      try {
+        statement.execute("create view tree_table (tag1 tag, tag2 tag) as 
root.**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: Cannot specify view pattern to match more than one tree 
database.",
+            e.getMessage());
+      }
+      statement.execute("create view tree_table (tag1 tag, tag2 tag) as 
root.\"重庆\".\"1\".**");
+      statement.execute("drop view tree_table");
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create timeSeries root.`重庆`.`1`.b.`1`.s1 int32");
+    } catch (SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("use tree_view_db");
+
+      try {
+        statement.execute("create view tree_table (tag1 tag, tag2 tag) as 
root.\"重庆\".\"1\".**");
+        fail();
+      } catch (final SQLException e) {
+        final Set<String> result =
+            new HashSet<>(
+                Arrays.asList(
+                    "617: The measurements s1 and S1 share the same lower case 
when auto detecting type, please check",
+                    "617: The measurements S1 and s1 share the same lower case 
when auto detecting type, please check"));
+        assertTrue(result.contains(e.getMessage()));
+      }
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("drop timeSeries root.`重庆`.`1`.b.`1`.s1");
+      statement.execute("create device template t1 (S1 boolean, s9 int32)");
+      statement.execute("set schema template t1 to root.`重庆`.`1`.b.`1`");
+      statement.execute("create timeSeries root.`重庆`.`1`.b.`2`.f.g.h.S1 
int32");
+
+      // Put schema cache
+      statement.execute("select S1, s2 from root.`重庆`.`1`.b.`2`");
+    } catch (SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("use tree_view_db");
+
+      try {
+        statement.execute("create view tree_table (tag1 tag, tag2 tag) as 
root.\"重庆\".\"1\".**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "614: Multiple types encountered when auto detecting type of 
measurement 'S1', please check",
+            e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create view tree_table (tag1 tag, tag2 tag, S1 field) as 
root.\"重庆\".\"1\".**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "614: Multiple types encountered when auto detecting type of 
measurement 'S1', please check",
+            e.getMessage());
+      }
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("create timeSeries root.`重庆`.`1`.b.e.s1 int32");
+    } catch (SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("use tree_view_db");
+
+      // Test error message
+      try {
+        statement.execute("alter view view_not_exist add column col from col");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'tree_view_db.view_not_exist' does not 
exist", e.getMessage());
+      }
+
+      // Temporary
+      try {
+        statement.execute(
+            "create or replace view tree_table (tag1 tag, tag2 tag, S1 int32 
field, s3 boolean from S1) as root.\"重庆\".\"1\".**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: The duplicated source measurement S1 is unsupported yet.", 
e.getMessage());
+      }
+
+      try {
+        statement.execute(
+            "create or replace view tree_table (tag1 tag, tag2 tag, S1 int32 
field, s3 from s2, s8 field) as root.\"重庆\".\"1\".**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("528: Measurements not found for s8, cannot auto detect", 
e.getMessage());
+      }
+
+      statement.execute(
+          "create or replace view tree_table (tag1 tag, tag2 tag, S1 int32 
field, s3 from s2) as root.\"重庆\".\"1\".**");
+
+      // Cannot be written
+      try {
+        statement.execute(
+            "insert into tree_table(time, tag1, tag2, S1, s3) values (1, 1, 1, 
1, 1)");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: The table tree_view_db.tree_table is a view from tree, 
cannot be written or deleted from",
+            e.getMessage());
+      }
+
+      statement.execute("alter view tree_table rename to view_table");
+
+      // Test clear cache
+      try {
+        statement.execute("select * from tree_table");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("550: Table 'tree_view_db.tree_table' does not exist.", 
e.getMessage());
+      }
+
+      statement.execute("alter view view_table rename column s1 to s11");
+      statement.execute("alter view view_table set properties ttl=100");
+      statement.execute("comment on view view_table is 'comment'");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show tables details"),
+          "TableName,TTL(ms),Status,Comment,TableType,",
+          Collections.singleton("view_table,100,USING,comment,VIEW FROM 
TREE,"));
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("desc view_table"),
+          "ColumnName,DataType,Category,",
+          new HashSet<>(
+              Arrays.asList(
+                  "time,TIMESTAMP,TIME,",
+                  "tag1,STRING,TAG,",
+                  "tag2,STRING,TAG,",
+                  "s11,INT32,FIELD,",
+                  "s3,STRING,FIELD,")));
+      // Currently we show the device even if all of its measurements does not 
match,
+      // the handling logic at query because validate it at fetching will 
potentially cause a
+      // lot of time
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from view_table where tag1 = 
'b'"),
+          "tag1,tag2,",
+          new HashSet<>(Arrays.asList("b,`2`,", "b,null,", "b,e,")));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from view_table where tag1 = 
'b' and tag2 is null"),
+          "tag1,tag2,",
+          Collections.singleton("b,null,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from view_table where tag1 = 
'b' and tag2 = '`2`'"),
+          "tag1,tag2,",
+          Collections.singleton("b,`2`,"));
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("count devices from view_table"),
+          "count(devices),",
+          Collections.singleton("3,"));
+    }
+
+    // Test tree session
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      // Test create & replace + restrict
+      statement.execute(
+          "create or replace view tree_view_db.view_table (tag1 tag, tag2 tag, 
s11 int32 field, s3 from s2) restrict with (ttl=100) as root.`重庆`.`1`.**");
+      fail();
+    } catch (final SQLException e) {
+      assertTrue(
+          e.getMessage().contains("The 'CreateTableView' is unsupported in 
tree sql-dialect."));
+    }
+
+    // Test permission
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      // Test create & replace + restrict
+      statement.execute("create user testUser 'testUser123456'");
+    } catch (final SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv()
+                .getConnection("testUser", "testUser123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute(
+          "create or replace view tree_view_db.view_table (tag1 tag, tag2 tag, 
s11 int32 field, s3 from s2) restrict with (ttl=100) as root.\"重庆\".\"1\".**");
+      fail();
+    } catch (final SQLException e) {
+      assertEquals(
+          "803: Access Denied: No permissions for this operation, please add 
privilege CREATE ON tree_view_db.view_table",
+          e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("grant create on tree_view_db.view_table to user 
testUser");
+    } catch (final SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv()
+                .getConnection("testUser", "testUser123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute(
+          "create or replace view tree_view_db.view_table (tag1 tag, tag2 tag, 
s11 int32 field, s3 from s2) restrict with (ttl=100) as root.\"重庆\".\"1\".**");
+      fail();
+    } catch (final SQLException e) {
+      assertEquals(
+          "803: Access Denied: No permissions for this operation, please add 
privilege READ_SCHEMA",
+          e.getMessage());
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("grant read_schema on root.`重庆`.** to user testUser");
+    } catch (final SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv()
+                .getConnection("testUser", "testUser123456", 
BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute(
+          "create or replace view tree_view_db.view_table (tag1 tag, tag2 tag, 
s11 int32 field, s3 from s2) restrict with (ttl=100) as root.\"重庆\".\"1\".**");
+      fail();
+    } catch (final SQLException e) {
+      assertEquals(
+          "803: Access Denied: No permissions for this operation, please add 
privilege READ_DATA",
+          e.getMessage());
+    }
+
+    try (final Connection connection = EnvFactory.getEnv().getConnection();
+        final Statement statement = connection.createStatement()) {
+      statement.execute("grant read_data on root.`重庆`.** to user testUser");
+    } catch (final SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("alter database tree_view_db set properties ttl=100");
+      statement.execute(
+          "create or replace view tree_view_db.view_table (tag1 tag, tag2 tag, 
s11 int32 field, s3 from s2) restrict as root.\"重庆\".\"1\".**");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show tables from tree_view_db"),
+          "TableName,TTL(ms),",
+          Collections.singleton("view_table,100,"));
+    } catch (final SQLException e) {
+      fail(e.getMessage());
+    }
+
+    try (final Connection connection =
+            EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        final Statement statement = connection.createStatement()) {
+      statement.execute("use tree_view_db");
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show devices from view_table where tag1 = 
'b' and tag2 is null"),
+          "tag1,tag2,",
+          Collections.emptySet());
+
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show create view view_table"),
+          "View,Create View,",
+          Collections.singleton(
+              "view_table,CREATE VIEW \"view_table\" (\"time\" TIMESTAMP 
TIME,\"tag1\" STRING TAG,\"tag2\" STRING TAG,\"s11\" INT32 FIELD,\"s3\" STRING 
FIELD FROM \"s2\") RESTRICT WITH (ttl=100) AS root.\"重庆\".\"1\".**,"));
+
+      // Can also use "show create table"
+      TestUtils.assertResultSetEqual(
+          statement.executeQuery("show create table view_table"),
+          "View,Create View,",
+          Collections.singleton(
+              "view_table,CREATE VIEW \"view_table\" (\"time\" TIMESTAMP 
TIME,\"tag1\" STRING TAG,\"tag2\" STRING TAG,\"s11\" INT32 FIELD,\"s3\" STRING 
FIELD FROM \"s2\") RESTRICT WITH (ttl=100) AS root.\"重庆\".\"1\".**,"));
+
+      statement.execute("create table a ()");
+      try {
+        statement.execute("show create view a");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals(
+            "701: The table a is a base table, does not support show create 
view.", e.getMessage());
+      }
+      try {
+        statement.execute("show create view information_schema.tables");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("701: The system view does not support show create.", 
e.getMessage());
+      }
+      try {
+        statement.execute("show create table information_schema.tables");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("701: The system view does not support show create.", 
e.getMessage());
+      }
+      try {
+        statement.execute("create or replace view a () as root.b.**");
+        fail();
+      } catch (final SQLException e) {
+        assertEquals("551: Table 'tree_view_db.a' already exists.", 
e.getMessage());
+      }
+    }
+  }
+}
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/view/CreateTableViewProcedure.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/view/CreateTableViewProcedure.java
new file mode 100644
index 00000000000..c6defba4c86
--- /dev/null
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/schema/table/view/CreateTableViewProcedure.java
@@ -0,0 +1,189 @@
+/*
+ * 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.confignode.procedure.impl.schema.table.view;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.exception.IoTDBException;
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.schema.table.TableNodeStatus;
+import org.apache.iotdb.commons.schema.table.TreeViewSchema;
+import org.apache.iotdb.commons.schema.table.TsTable;
+import 
org.apache.iotdb.confignode.consensus.request.write.table.view.PreCreateTableViewPlan;
+import org.apache.iotdb.confignode.exception.DatabaseNotExistsException;
+import 
org.apache.iotdb.confignode.persistence.schema.TreeDeviceViewFieldDetector;
+import org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv;
+import org.apache.iotdb.confignode.procedure.exception.ProcedureException;
+import org.apache.iotdb.confignode.procedure.impl.schema.SchemaUtils;
+import 
org.apache.iotdb.confignode.procedure.impl.schema.table.CreateTableProcedure;
+import org.apache.iotdb.confignode.procedure.state.schema.CreateTableState;
+import org.apache.iotdb.confignode.procedure.store.ProcedureType;
+import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import org.apache.tsfile.utils.Pair;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Objects;
+import java.util.Optional;
+
+import static org.apache.iotdb.rpc.TSStatusCode.TABLE_ALREADY_EXISTS;
+
+public class CreateTableViewProcedure extends CreateTableProcedure {
+  private static final Logger LOGGER = 
LoggerFactory.getLogger(CreateTableViewProcedure.class);
+  private boolean replace;
+  private TsTable oldView;
+  private TableNodeStatus oldStatus;
+
+  public CreateTableViewProcedure(final boolean isGeneratedByPipe) {
+    super(isGeneratedByPipe);
+  }
+
+  public CreateTableViewProcedure(
+      final String database,
+      final TsTable table,
+      final boolean replace,
+      final boolean isGeneratedByPipe) {
+    super(database, table, isGeneratedByPipe);
+    this.replace = replace;
+  }
+
+  @Override
+  protected void checkTableExistence(final ConfigNodeProcedureEnv env) {
+    if (!replace) {
+      super.checkTableExistence(env);
+    } else {
+      try {
+        final Optional<Pair<TsTable, TableNodeStatus>> oldTableAndStatus =
+            env.getConfigManager()
+                .getClusterSchemaManager()
+                .getTableAndStatusIfExists(database, table.getTableName());
+        if (oldTableAndStatus.isPresent()) {
+          if 
(!TreeViewSchema.isTreeViewTable(oldTableAndStatus.get().getLeft())) {
+            setFailure(
+                new ProcedureException(
+                    new IoTDBException(
+                        String.format(
+                            "Table '%s.%s' already exists.", database, 
table.getTableName()),
+                        TABLE_ALREADY_EXISTS.getStatusCode())));
+            return;
+          } else {
+            oldView = oldTableAndStatus.get().getLeft();
+            oldStatus = oldTableAndStatus.get().getRight();
+          }
+        }
+        final TDatabaseSchema schema =
+            
env.getConfigManager().getClusterSchemaManager().getDatabaseSchemaByName(database);
+        if (!table.getPropValue(TsTable.TTL_PROPERTY).isPresent()
+            && schema.isSetTTL()
+            && schema.getTTL() != Long.MAX_VALUE) {
+          table.addProp(TsTable.TTL_PROPERTY, String.valueOf(schema.getTTL()));
+        }
+        setNextState(CreateTableState.PRE_CREATE);
+      } catch (final MetadataException | DatabaseNotExistsException e) {
+        setFailure(new ProcedureException(e));
+      }
+    }
+    final TSStatus status =
+        new TreeDeviceViewFieldDetector(env.getConfigManager(), table, null)
+            .detectMissingFieldTypes();
+    if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      setFailure(new ProcedureException(new IoTDBException(status)));
+    }
+  }
+
+  @Override
+  protected void preCreateTable(final ConfigNodeProcedureEnv env) {
+    final TSStatus status =
+        SchemaUtils.executeInConsensusLayer(
+            new PreCreateTableViewPlan(database, table, 
TableNodeStatus.PRE_CREATE), env, LOGGER);
+    if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      setNextState(CreateTableState.PRE_RELEASE);
+    } else {
+      setFailure(new ProcedureException(new IoTDBException(status)));
+    }
+  }
+
+  @Override
+  protected void rollbackCreate(final ConfigNodeProcedureEnv env) {
+    if (Objects.isNull(oldView)) {
+      super.rollbackCreate(env);
+      return;
+    }
+    final TSStatus status =
+        SchemaUtils.executeInConsensusLayer(
+            new PreCreateTableViewPlan(database, oldView, oldStatus), env, 
LOGGER);
+    if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+      LOGGER.warn("Failed to rollback table creation {}.{}", database, 
table.getTableName());
+      setFailure(new ProcedureException(new IoTDBException(status)));
+    }
+  }
+
+  @Override
+  public void serialize(final DataOutputStream stream) throws IOException {
+    stream.writeShort(
+        isGeneratedByPipe
+            ? 
ProcedureType.PIPE_ENRICHED_CREATE_TABLE_VIEW_PROCEDURE.getTypeCode()
+            : ProcedureType.CREATE_TABLE_VIEW_PROCEDURE.getTypeCode());
+    innerSerialize(stream);
+    ReadWriteIOUtils.write(replace, stream);
+
+    ReadWriteIOUtils.write(Objects.nonNull(oldView), stream);
+    if (Objects.nonNull(oldView)) {
+      oldView.serialize(stream);
+    }
+
+    ReadWriteIOUtils.write(Objects.nonNull(oldStatus), stream);
+    if (Objects.nonNull(oldStatus)) {
+      oldStatus.serialize(stream);
+    }
+  }
+
+  @Override
+  public void deserialize(final ByteBuffer byteBuffer) {
+    super.deserialize(byteBuffer);
+    replace = ReadWriteIOUtils.readBool(byteBuffer);
+
+    if (ReadWriteIOUtils.readBool(byteBuffer)) {
+      this.oldView = TsTable.deserialize(byteBuffer);
+    }
+
+    if (ReadWriteIOUtils.readBool(byteBuffer)) {
+      this.oldStatus = TableNodeStatus.deserialize(byteBuffer);
+    }
+  }
+
+  @Override
+  public boolean equals(final Object o) {
+    return super.equals(o)
+        && replace == ((CreateTableViewProcedure) o).replace
+        && Objects.equals(oldView, ((CreateTableViewProcedure) o).oldView)
+        && Objects.equals(oldStatus, ((CreateTableViewProcedure) o).oldStatus);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(super.hashCode(), replace, oldView, oldStatus);
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java
index 3c03ee732d0..367c8327421 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/realtime/PipeRealtimeDataRegionSource.java
@@ -382,8 +382,11 @@ public abstract class PipeRealtimeDataRegionSource 
implements PipeExtractor {
       }
       pendingQueue.pollLast();
     }
-    if (pendingQueue.peekLast() instanceof ProgressReportEvent) {
-      final ProgressReportEvent oldEvent = (ProgressReportEvent) 
pendingQueue.peekLast();
+    final Event last = pendingQueue.peekLast();
+    if (last instanceof PipeRealtimeEvent
+        && ((PipeRealtimeEvent) last).getEvent() instanceof 
ProgressReportEvent) {
+      final ProgressReportEvent oldEvent =
+          (ProgressReportEvent) ((PipeRealtimeEvent) last).getEvent();
       oldEvent.bindProgressIndex(
           oldEvent
               .getProgressIndex()
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java
new file mode 100644
index 00000000000..5acb26d90fc
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTableTask.java
@@ -0,0 +1,157 @@
+/*
+ * 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.schema.column.ColumnHeader;
+import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
+import org.apache.iotdb.commons.schema.table.TreeViewSchema;
+import org.apache.iotdb.commons.schema.table.TsTable;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory;
+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.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.tsfile.utils.Binary;
+
+import javax.annotation.Nonnull;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.iotdb.commons.conf.IoTDBConstant.TTL_INFINITE;
+
+public class ShowCreateTableTask extends AbstractTableTask {
+  public ShowCreateTableTask(final String database, final String tableName) {
+    super(database, tableName);
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(final IConfigTaskExecutor 
configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.describeTable(database, tableName, false, false);
+  }
+
+  public static void buildTsBlock(
+      final TsTable table, final SettableFuture<ConfigTaskResult> future) {
+    if (TreeViewSchema.isTreeViewTable(table)) {
+      ShowCreateViewTask.buildTsBlock(table, future);
+      return;
+    }
+    final List<TSDataType> outputDataTypes =
+        ColumnHeaderConstant.showCreateTableColumnHeaders.stream()
+            .map(ColumnHeader::getColumnType)
+            .collect(Collectors.toList());
+
+    final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes);
+    builder.getTimeColumnBuilder().writeLong(0L);
+    builder
+        .getColumnBuilder(0)
+        .writeBinary(new Binary(table.getTableName(), 
TSFileConfig.STRING_CHARSET));
+    builder
+        .getColumnBuilder(1)
+        .writeBinary(new Binary(getShowCreateTableSQL(table), 
TSFileConfig.STRING_CHARSET));
+    builder.declarePosition();
+
+    final DatasetHeader datasetHeader = 
DatasetHeaderFactory.getShowCreateTableColumnHeader();
+    future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, 
builder.build(), datasetHeader));
+  }
+
+  private static String getShowCreateTableSQL(final TsTable table) {
+    final StringBuilder builder =
+        new StringBuilder("CREATE TABLE 
").append(getIdentifier(table.getTableName())).append(" (");
+
+    for (final TsTableColumnSchema schema : table.getColumnList()) {
+      switch (schema.getColumnCategory()) {
+        case TAG:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("TAG");
+          break;
+        case TIME:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("TIME");
+          break;
+        case FIELD:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("FIELD");
+          break;
+        case ATTRIBUTE:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("ATTRIBUTE");
+          break;
+        default:
+          throw new UnsupportedOperationException(
+              "Unsupported column type: " + schema.getColumnCategory());
+      }
+      if (Objects.nonNull(schema.getProps().get(TsTable.COMMENT_KEY))) {
+        builder.append(" COMMENT 
").append(getString(schema.getProps().get(TsTable.COMMENT_KEY)));
+      }
+      builder.append(",");
+    }
+
+    if (!table.getColumnList().isEmpty()) {
+      builder.deleteCharAt(builder.length() - 1);
+    }
+
+    builder.append(")");
+    if (table.getPropValue(TsTable.COMMENT_KEY).isPresent()) {
+      builder.append(" COMMENT 
").append(getString(table.getPropValue(TsTable.COMMENT_KEY).get()));
+    }
+
+    String ttlString = 
table.getPropValue(TsTable.TTL_PROPERTY).orElse(TTL_INFINITE);
+    if (ttlString.equals(TTL_INFINITE)) {
+      ttlString = "'" + ttlString + "'";
+    }
+    builder.append(" WITH (ttl=").append(ttlString).append(")");
+
+    return builder.toString();
+  }
+
+  public static String getIdentifier(@Nonnull final String identifier) {
+    return "\"" + identifier.replace("\"", "\"\"") + "\"";
+  }
+
+  public static String getString(@Nonnull final String string) {
+    return "'" + string.replace("'", "''") + "'";
+  }
+}
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java
new file mode 100644
index 00000000000..9fc573f8988
--- /dev/null
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateViewTask.java
@@ -0,0 +1,162 @@
+/*
+ * 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.exception.SemanticException;
+import org.apache.iotdb.commons.schema.column.ColumnHeader;
+import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
+import org.apache.iotdb.commons.schema.table.TreeViewSchema;
+import org.apache.iotdb.commons.schema.table.TsTable;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
+import org.apache.iotdb.db.queryengine.common.header.DatasetHeaderFactory;
+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.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.apache.tsfile.common.conf.TSFileConfig;
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.read.common.block.TsBlockBuilder;
+import org.apache.tsfile.utils.Binary;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static org.apache.iotdb.commons.conf.IoTDBConstant.TTL_INFINITE;
+import static 
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask.getIdentifier;
+import static 
org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask.getString;
+
+public class ShowCreateViewTask extends AbstractTableTask {
+  public ShowCreateViewTask(final String database, final String tableName) {
+    super(database, tableName);
+  }
+
+  @Override
+  public ListenableFuture<ConfigTaskResult> execute(final IConfigTaskExecutor 
configTaskExecutor)
+      throws InterruptedException {
+    return configTaskExecutor.describeTable(database, tableName, false, true);
+  }
+
+  public static void buildTsBlock(
+      final TsTable table, final SettableFuture<ConfigTaskResult> future) {
+    if (!TreeViewSchema.isTreeViewTable(table)) {
+      throw new SemanticException(
+          "The table "
+              + table.getTableName()
+              + " is a base table, does not support show create view.");
+    }
+    final List<TSDataType> outputDataTypes =
+        ColumnHeaderConstant.showCreateViewColumnHeaders.stream()
+            .map(ColumnHeader::getColumnType)
+            .collect(Collectors.toList());
+
+    final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes);
+    builder.getTimeColumnBuilder().writeLong(0L);
+    builder
+        .getColumnBuilder(0)
+        .writeBinary(new Binary(table.getTableName(), 
TSFileConfig.STRING_CHARSET));
+    builder
+        .getColumnBuilder(1)
+        .writeBinary(new Binary(getShowCreateViewSQL(table), 
TSFileConfig.STRING_CHARSET));
+    builder.declarePosition();
+
+    final DatasetHeader datasetHeader = 
DatasetHeaderFactory.getShowCreateViewColumnHeader();
+    future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, 
builder.build(), datasetHeader));
+  }
+
+  public static String getShowCreateViewSQL(final TsTable table) {
+    final StringBuilder builder =
+        new StringBuilder("CREATE VIEW 
").append(getIdentifier(table.getTableName())).append(" (");
+
+    for (final TsTableColumnSchema schema : table.getColumnList()) {
+      switch (schema.getColumnCategory()) {
+        case TAG:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("TAG");
+          break;
+        case TIME:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("TIME");
+          break;
+        case FIELD:
+          builder
+              .append(getIdentifier(schema.getColumnName()))
+              .append(" ")
+              .append(schema.getDataType())
+              .append(" ")
+              .append("FIELD");
+          if (Objects.nonNull(TreeViewSchema.getOriginalName(schema))) {
+            builder.append(" FROM 
").append(getIdentifier(TreeViewSchema.getOriginalName(schema)));
+          }
+          break;
+        case ATTRIBUTE:
+        default:
+          throw new UnsupportedOperationException(
+              "Unsupported column type: " + schema.getColumnCategory());
+      }
+      if (Objects.nonNull(schema.getProps().get(TsTable.COMMENT_KEY))) {
+        builder.append(" COMMENT 
").append(getString(schema.getProps().get(TsTable.COMMENT_KEY)));
+      }
+      builder.append(",");
+    }
+
+    if (!table.getColumnList().isEmpty()) {
+      builder.deleteCharAt(builder.length() - 1);
+    }
+
+    builder.append(")");
+
+    if (table.getPropValue(TsTable.COMMENT_KEY).isPresent()) {
+      builder.append(" COMMENT 
").append(getString(table.getPropValue(TsTable.COMMENT_KEY).get()));
+    }
+
+    if (TreeViewSchema.isRestrict(table)) {
+      builder.append(" RESTRICT");
+    }
+
+    String ttlString = 
table.getPropValue(TsTable.TTL_PROPERTY).orElse(TTL_INFINITE);
+    if (ttlString.equals(TTL_INFINITE)) {
+      ttlString = "'" + ttlString + "'";
+    }
+    builder.append(" WITH (ttl=").append(ttlString).append(")");
+
+    builder.append(" AS ");
+
+    final String[] pathNodes = 
TreeViewSchema.getPrefixPattern(table).getNodes();
+    builder.append(pathNodes[0]);
+    for (int i = 1; i < pathNodes.length - 1; ++i) {
+      builder.append(".\"").append(pathNodes[i].replace("`", "")).append("\"");
+    }
+    builder.append(".").append(pathNodes[pathNodes.length - 1]);
+
+    return builder.toString();
+  }
+}
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
new file mode 100644
index 00000000000..4a82bdd70a0
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/TsTable.java
@@ -0,0 +1,431 @@
+/*
+ * 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.commons.schema.table;
+
+import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.exception.MetadataException;
+import org.apache.iotdb.commons.exception.runtime.SchemaExecutionException;
+import org.apache.iotdb.commons.schema.table.column.TimeColumnSchema;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema;
+import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchemaUtil;
+import org.apache.iotdb.commons.utils.CommonDateTimeUtils;
+
+import com.google.common.collect.ImmutableList;
+import org.apache.tsfile.utils.Pair;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
+
+import javax.annotation.concurrent.ThreadSafe;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+import static org.apache.iotdb.commons.conf.IoTDBConstant.TTL_INFINITE;
+
+@ThreadSafe
+public class TsTable {
+
+  public static final String TIME_COLUMN_NAME = "time";
+  public static final String COMMENT_KEY = "__comment";
+  public static final String TTL_PROPERTY = "ttl";
+  public static final Set<String> TABLE_ALLOWED_PROPERTIES = 
Collections.singleton(TTL_PROPERTY);
+  private static final String OBJECT_STRING_ERROR =
+      "When there are object fields, the %s %s shall not be '.', '..' or 
contain './', '.\\'.";
+  protected String tableName;
+
+  private final Map<String, TsTableColumnSchema> columnSchemaMap = new 
LinkedHashMap<>();
+  private final Map<String, Integer> tagColumnIndexMap = new HashMap<>();
+
+  private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
+
+  /**
+   * Global sequence generator providing unique, monotonically increasing IDs 
across all instances.
+   * Initialized to -1 to ensure the first ID is 0.
+   */
+  private static final AtomicLong GLOBAL_SEQUENCE = new AtomicLong(-1);
+
+  private final transient Long creationId = GLOBAL_SEQUENCE.getAndIncrement();
+  private final transient AtomicLong instanceVersion = new AtomicLong(0L);
+
+  private final transient AtomicBoolean isNotWrite = new AtomicBoolean(true);
+  private final AtomicReference<Pair<Long, List<TsTableColumnSchema>>> 
tagColumnSchemas =
+      new AtomicReference<>();
+
+  private Map<String, String> props = null;
+
+  // Cache, avoid string parsing
+  private transient long ttlValue = Long.MIN_VALUE;
+  private transient int tagNums = 0;
+  private transient int fieldNum = 0;
+
+  // Initiated during creation and never changed the reference
+  private transient TsTableColumnSchema timeColumnSchema;
+
+  public TsTable(final String tableName) {
+    this.tableName = tableName;
+  }
+
+  // This interface is used by InformationSchema table, so time column is not 
necessary
+  public TsTable(String tableName, ImmutableList<TsTableColumnSchema> 
columnSchemas) {
+    this.tableName = tableName;
+    columnSchemas.forEach(
+        columnSchema -> {
+          columnSchemaMap.put(columnSchema.getColumnName(), columnSchema);
+          if (columnSchema instanceof TimeColumnSchema) {
+            timeColumnSchema = columnSchema;
+          }
+        });
+  }
+
+  public TsTable(TsTable origin) {
+    this.tableName = origin.tableName;
+    origin.columnSchemaMap.forEach((col, schema) -> 
this.columnSchemaMap.put(col, schema.copy()));
+    this.tagColumnIndexMap.putAll(origin.tagColumnIndexMap);
+    this.props = origin.props == null ? null : new HashMap<>(origin.props);
+    this.ttlValue = origin.ttlValue;
+    this.tagNums = origin.tagNums;
+    this.fieldNum = origin.fieldNum;
+  }
+
+  public String getTableName() {
+    return tableName;
+  }
+
+  /**
+   * Get column schema with optimistic lock for fast reads. This method uses a 
lock-free fast path
+   * when there's no concurrent write operation, significantly improving read 
performance.
+   *
+   * @param columnName the column name to query
+   * @return the column schema, or null if not found
+   */
+  public TsTableColumnSchema getColumnSchema(final String columnName) {
+    final long versionBefore = instanceVersion.get();
+    final TsTableColumnSchema result = columnSchemaMap.get(columnName);
+    if (isNotWrite.get() && instanceVersion.get() == versionBefore) {
+      return result;
+    }
+
+    // Slow path: write in progress or version changed, acquire read lock
+    readWriteLock.readLock().lock();
+    try {
+      return columnSchemaMap.get(columnName);
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  // No need to acquire lock, because the time column is fixed after table 
creation
+  // And the inner name is protected by the volatile keyword
+  public TsTableColumnSchema getTimeColumnSchema() {
+    if (Objects.isNull(timeColumnSchema)) {
+      timeColumnSchema =
+          columnSchemaMap.values().stream()
+              .filter(column -> column instanceof TimeColumnSchema)
+              .findFirst()
+              .orElse(null);
+    }
+    return timeColumnSchema;
+  }
+
+  /**
+   * Execute a write operation with optimistic lock support. This method 
handles the write flag and
+   * version increment automatically.
+   *
+   * @param writeOperation the write operation to execute
+   */
+  private void executeWrite(Runnable writeOperation) {
+    readWriteLock.writeLock().lock();
+    isNotWrite.set(false);
+    try {
+      writeOperation.run();
+    } finally {
+      instanceVersion.incrementAndGet();
+      isNotWrite.set(true);
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  public int getTagColumnOrdinal(final String columnName) {
+    readWriteLock.readLock().lock();
+    try {
+      return tagColumnIndexMap.getOrDefault(columnName.toLowerCase(), -1);
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public List<TsTableColumnSchema> getTagColumnSchemaList() {
+    Pair<Long, List<TsTableColumnSchema>> VersionAndTagColumnSchemas = 
tagColumnSchemas.get();
+    if (VersionAndTagColumnSchemas != null
+        && isNotWrite.get()
+        && VersionAndTagColumnSchemas.getLeft() == instanceVersion.get()) {
+      return VersionAndTagColumnSchemas.getRight();
+    }
+
+    readWriteLock.readLock().lock();
+    try {
+      List<TsTableColumnSchema> tagColumnSchemaList = new 
ArrayList<>(tagColumnIndexMap.size());
+      for (final TsTableColumnSchema columnSchema : columnSchemaMap.values()) {
+        if 
(TsTableColumnCategory.TAG.equals(columnSchema.getColumnCategory())) {
+          tagColumnSchemaList.add(columnSchema);
+        }
+      }
+      VersionAndTagColumnSchemas = new Pair<>(instanceVersion.get(), 
tagColumnSchemaList);
+      return tagColumnSchemaList;
+    } finally {
+      tagColumnSchemas.set(VersionAndTagColumnSchemas);
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  // Currently only supports device view
+  public void renameTable(final String newName) {
+    executeWrite(() -> tableName = newName);
+  }
+
+  public void addColumnSchema(final TsTableColumnSchema columnSchema) {
+    executeWrite(
+        () -> {
+          columnSchemaMap.put(columnSchema.getColumnName(), columnSchema);
+          if 
(columnSchema.getColumnCategory().equals(TsTableColumnCategory.TAG)) {
+            tagNums++;
+            tagColumnIndexMap.put(columnSchema.getColumnName(), tagNums - 1);
+          } else if 
(columnSchema.getColumnCategory().equals(TsTableColumnCategory.FIELD)) {
+            fieldNum++;
+          }
+        });
+  }
+
+  public void renameColumnSchema(final String oldName, final String newName) {
+    executeWrite(
+        () -> {
+          // Ensures idempotency
+          if (columnSchemaMap.containsKey(oldName)) {
+            final TsTableColumnSchema schema = columnSchemaMap.get(oldName);
+            final Map<String, String> oldProps = schema.getProps();
+            oldProps.computeIfAbsent(TreeViewSchema.ORIGINAL_NAME, k -> 
schema.getColumnName());
+            schema.setColumnName(newName);
+          }
+        });
+  }
+
+  public void removeColumnSchema(final String columnName) {
+    executeWrite(
+        () -> {
+          final TsTableColumnSchema columnSchema = 
columnSchemaMap.get(columnName);
+          if (columnSchema != null
+              && 
columnSchema.getColumnCategory().equals(TsTableColumnCategory.TAG)) {
+            throw new SchemaExecutionException("Cannot remove an tag column: " 
+ columnName);
+          } else if (columnSchema != null) {
+            columnSchemaMap.remove(columnName);
+            if 
(columnSchema.getColumnCategory().equals(TsTableColumnCategory.FIELD)) {
+              fieldNum--;
+            }
+          }
+        });
+  }
+
+  public int getColumnNum() {
+    readWriteLock.readLock().lock();
+    try {
+      return columnSchemaMap.size();
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public int getTagNum() {
+    readWriteLock.readLock().lock();
+    try {
+      return tagNums;
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public int getFieldNum() {
+    readWriteLock.readLock().lock();
+    try {
+      return fieldNum;
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public List<TsTableColumnSchema> getColumnList() {
+    readWriteLock.readLock().lock();
+    try {
+      return new ArrayList<>(columnSchemaMap.values());
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  // This shall only be called on DataNode, where the tsTable is replaced 
completely thus an old
+  // cache won't pollute the newest value
+  public long getCachedTableTTL() {
+    // Cache for performance
+    if (ttlValue < 0) {
+      ttlValue = getTableTTL();
+    }
+    return ttlValue;
+  }
+
+  public long getTableTTL() {
+    final Optional<String> ttl = getPropValue(TTL_PROPERTY);
+    return ttl.isPresent() && !ttl.get().equalsIgnoreCase(TTL_INFINITE)
+        ? CommonDateTimeUtils.convertMilliTimeWithPrecision(
+            Long.parseLong(ttl.get()),
+            CommonDescriptor.getInstance().getConfig().getTimestampPrecision())
+        : Long.MAX_VALUE;
+  }
+
+  public Map<String, String> getProps() {
+    readWriteLock.readLock().lock();
+    try {
+      return props;
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public Pair<Long, Long> getInstanceVersion() {
+    return new Pair<>(creationId, instanceVersion.get());
+  }
+
+  public boolean containsPropWithoutLock(final String propKey) {
+    return props != null && props.containsKey(propKey);
+  }
+
+  public Optional<String> getPropValue(final String propKey) {
+    readWriteLock.readLock().lock();
+    try {
+      return props != null && props.containsKey(propKey)
+          ? Optional.of(props.get(propKey))
+          : Optional.empty();
+    } finally {
+      readWriteLock.readLock().unlock();
+    }
+  }
+
+  public void addProp(final String key, final String value) {
+    executeWrite(
+        () -> {
+          if (props == null) {
+            props = new HashMap<>();
+          }
+          props.put(key, value);
+        });
+  }
+
+  public void removeProp(final String key) {
+    executeWrite(
+        () -> {
+          if (props == null) {
+            return;
+          }
+          props.remove(key);
+        });
+  }
+
+  public void serialize(final OutputStream stream) throws IOException {
+    ReadWriteIOUtils.write(tableName, stream);
+    ReadWriteIOUtils.write(columnSchemaMap.size(), stream);
+    for (final TsTableColumnSchema columnSchema : columnSchemaMap.values()) {
+      TsTableColumnSchemaUtil.serialize(columnSchema, stream);
+    }
+    ReadWriteIOUtils.write(props, stream);
+  }
+
+  public static TsTable deserialize(final InputStream inputStream) throws 
IOException {
+    final String name = ReadWriteIOUtils.readString(inputStream);
+    final int columnNum = ReadWriteIOUtils.readInt(inputStream);
+    if (columnNum < 0) {
+      return new NonCommittableTsTable(name);
+    }
+    final TsTable table = new TsTable(name);
+    for (int i = 0; i < columnNum; i++) {
+      table.addColumnSchema(TsTableColumnSchemaUtil.deserialize(inputStream));
+    }
+    table.props = ReadWriteIOUtils.readMap(inputStream);
+    return table;
+  }
+
+  public static TsTable deserialize(final ByteBuffer buffer) {
+    final String name = ReadWriteIOUtils.readString(buffer);
+    final int columnNum = ReadWriteIOUtils.readInt(buffer);
+    if (columnNum < 0) {
+      return new NonCommittableTsTable(name);
+    }
+    final TsTable table = new TsTable(name);
+    for (int i = 0; i < columnNum; i++) {
+      table.addColumnSchema(TsTableColumnSchemaUtil.deserialize(buffer));
+    }
+    table.props = ReadWriteIOUtils.readMap(buffer);
+    return table;
+  }
+
+  public void setProps(Map<String, String> props) {
+    executeWrite(() -> this.props = props);
+  }
+
+  public void checkTableNameAndObjectNames4Object() throws MetadataException {
+    throw new MetadataException("The object type column is not supported.");
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    return super.equals(o);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(tableName);
+  }
+
+  @Override
+  public String toString() {
+    return "TsTable{"
+        + "tableName='"
+        + tableName
+        + '\''
+        + ", columnSchemaMap="
+        + columnSchemaMap
+        + ", props="
+        + props
+        + '}';
+  }
+}
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java
new file mode 100644
index 00000000000..8342c08365f
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/column/TsTableColumnSchema.java
@@ -0,0 +1,112 @@
+/*
+ * 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.commons.schema.table.column;
+
+import org.apache.tsfile.enums.TSDataType;
+import org.apache.tsfile.utils.ReadWriteIOUtils;
+import org.apache.tsfile.write.schema.IMeasurementSchema;
+import org.apache.tsfile.write.schema.MeasurementSchema;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+
+public abstract class TsTableColumnSchema {
+
+  protected volatile String columnName;
+
+  protected TSDataType dataType;
+
+  protected Map<String, String> props = null;
+
+  TsTableColumnSchema(final String columnName, final TSDataType dataType) {
+    this.columnName = columnName;
+    this.dataType = dataType;
+  }
+
+  TsTableColumnSchema(
+      final String columnName, final TSDataType dataType, final Map<String, 
String> props) {
+    this.columnName = columnName;
+    this.dataType = dataType;
+    this.props = props;
+  }
+
+  // Only used for column renaming
+  public TsTableColumnSchema setColumnName(String columnName) {
+    this.columnName = columnName;
+    return this;
+  }
+
+  public String getColumnName() {
+    return columnName;
+  }
+
+  public TSDataType getDataType() {
+    return dataType;
+  }
+
+  public Map<String, String> getProps() {
+    if (Objects.isNull(props)) {
+      props = new HashMap<>();
+    }
+    return props;
+  }
+
+  public abstract TsTableColumnCategory getColumnCategory();
+
+  public IMeasurementSchema getMeasurementSchema() {
+    return new MeasurementSchema(columnName, dataType);
+  }
+
+  void serialize(final OutputStream outputStream) throws IOException {
+    ReadWriteIOUtils.write(columnName, outputStream);
+    ReadWriteIOUtils.write(dataType, outputStream);
+    ReadWriteIOUtils.write(props, outputStream);
+  }
+
+  @Override
+  public boolean equals(final Object o) {
+    return super.equals(o);
+  }
+
+  @Override
+  public int hashCode() {
+    return Objects.hash(columnName);
+  }
+
+  public void setDataType(final TSDataType dataType) {
+    this.dataType = dataType;
+  }
+
+  public abstract TsTableColumnSchema copy();
+
+  @Override
+  public String toString() {
+    return toStringHelper(this)
+        .add("columnName", columnName)
+        .add("dataType", dataType)
+        .add("props", props)
+        .toString();
+  }
+}

Reply via email to