From aca2e132e1306a459b07be31ecbeb030c68c59c3 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Mon, 18 May 2026 12:09:17 +0800 Subject: [PATCH 1/2] ut --- .../relational/it/schema/IoTDBDatabaseIT.java | 45 +++++ .../common/header/DatasetHeaderFactory.java | 8 + .../config/TableConfigTaskVisitor.java | 19 ++ .../executor/ClusterConfigTaskExecutor.java | 104 +++++++++++ .../config/executor/IConfigTaskExecutor.java | 4 + .../relational/ShowCreateDatabaseTask.java | 96 ++++++++++ .../relational/ShowCreatePipeTask.java | 176 ++++++++++++++++++ .../analyzer/StatementAnalyzer.java | 12 ++ .../plan/relational/sql/ast/AstVisitor.java | 8 + .../sql/ast/ShowCreateDatabase.java | 92 +++++++++ .../relational/sql/ast/ShowCreatePipe.java | 84 +++++++++ .../relational/sql/parser/AstBuilder.java | 14 ++ .../sql/util/DataNodeSqlFormatter.java | 19 ++ .../relational/ShowCreateTaskTest.java | 146 +++++++++++++++ .../schema/column/ColumnHeaderConstant.java | 12 ++ .../relational/grammar/sql/RelationalSql.g4 | 10 + 16 files changed, 849 insertions(+) create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java diff --git a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java index 2b367b70b2212..ebed28f043333 100644 --- a/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/relational/it/schema/IoTDBDatabaseIT.java @@ -285,6 +285,51 @@ public void testManageDatabase() { } } + @Test + public void testShowCreateDatabase() throws SQLException { + try (final Connection connection = + EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + final Statement statement = connection.createStatement()) { + statement.execute( + "create database test_show_create_db with (ttl=300, schema_region_group_num=DEFAULT, data_region_group_num=DEFAULT, time_partition_interval=100000)"); + + TestUtils.assertResultSetEqual( + statement.executeQuery("show create database test_show_create_db"), + "Database,Create Database,", + Collections.singleton( + "test_show_create_db,CREATE DATABASE \"test_show_create_db\" WITH (ttl=300,time_partition_interval=100000,schema_region_group_num=0,data_region_group_num=0),")); + } + } + + @Test + public void testShowCreatePipe() throws SQLException { + try (final Connection connection = + EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + final Statement statement = connection.createStatement()) { + statement.execute("create pipe test_show_create_pipe ('sink'='do-nothing-sink')"); + + TestUtils.assertResultSetEqual( + statement.executeQuery("show create pipe test_show_create_pipe"), + "Pipe,Create Pipe,", + Collections.singleton( + "test_show_create_pipe,CREATE PIPE \"test_show_create_pipe\" WITH SINK ('sink'='do-nothing-sink'),")); + } + } + + @Test + public void testShowCreateInformationSchemaDatabase() throws SQLException { + try (final Connection connection = + EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + final Statement statement = connection.createStatement()) { + try { + statement.executeQuery("show create database information_schema"); + fail("show create database information_schema shouldn't succeed"); + } catch (final SQLException e) { + assertEquals("701: The system database does not support show create.", e.getMessage()); + } + } + } + @Test public void testDatabaseWithSpecificCharacters() throws SQLException { try (final Connection connection = diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java index 18f15eea8f397..b2c327b866aa7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java @@ -263,6 +263,14 @@ public static DatasetHeader getShowCreateTableColumnHeader() { return new DatasetHeader(ColumnHeaderConstant.showCreateTableColumnHeaders, true); } + public static DatasetHeader getShowCreatePipeColumnHeader() { + return new DatasetHeader(ColumnHeaderConstant.showCreatePipeColumnHeaders, true); + } + + public static DatasetHeader getShowCreateDatabaseColumnHeader() { + return new DatasetHeader(ColumnHeaderConstant.showCreateDatabaseColumnHeaders, true); + } + public static DatasetHeader getShowTablesHeader() { return new DatasetHeader(ColumnHeaderConstant.showTablesColumnHeaders, true); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index e07bc9203139e..4400b039d9a67 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -110,6 +110,8 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.RelationalAuthorizerTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowAINodesTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowConfigNodesTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateDatabaseTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreatePipeTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateViewTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowDBTask; @@ -209,6 +211,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfigNodes; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfiguration; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -424,6 +428,15 @@ public IConfigTask visitShowDB(final ShowDB node, final MPPQueryContext context) canShowDB(accessControl, context.getSession().getUserName(), databaseName, context)); } + @Override + public IConfigTask visitShowCreateDatabase( + final ShowCreateDatabase node, final MPPQueryContext context) { + context.setQueryType(QueryType.READ); + accessControl.checkCanShowOrUseDatabase( + context.getSession().getUserName(), node.getDatabase(), context); + return new ShowCreateDatabaseTask(node.getDatabase()); + } + public static boolean canShowDB( final AccessControl accessControl, final String userName, @@ -1341,6 +1354,12 @@ public IConfigTask visitShowPipes(ShowPipes node, MPPQueryContext context) { return new ShowPipeTask(node, context.getSession().getUserName()); } + @Override + public IConfigTask visitShowCreatePipe(ShowCreatePipe node, MPPQueryContext context) { + context.setQueryType(QueryType.READ); + return new ShowCreatePipeTask(node.getPipeName(), context.getSession().getUserName()); + } + @Override public IConfigTask visitCreatePipePlugin(CreatePipePlugin node, MPPQueryContext context) { context.setQueryType(QueryType.OTHER); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 3f632c9a98735..274eb5682334b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -81,6 +81,7 @@ import org.apache.iotdb.commons.schema.column.ColumnHeader; import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.commons.schema.table.AlterOrDropTableOperationType; +import org.apache.iotdb.commons.schema.table.InformationSchema; import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.commons.schema.table.TsTableInternalRPCUtil; import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema; @@ -118,6 +119,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TCreateTriggerReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRemoveReq; import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRemoveResp; +import org.apache.iotdb.confignode.rpc.thrift.TDatabaseInfo; import org.apache.iotdb.confignode.rpc.thrift.TDatabaseSchema; import org.apache.iotdb.confignode.rpc.thrift.TDeactivateSchemaTemplateReq; import org.apache.iotdb.confignode.rpc.thrift.TDeleteDatabasesReq; @@ -162,6 +164,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TShowPipeInfo; import org.apache.iotdb.confignode.rpc.thrift.TShowPipePluginReq; import org.apache.iotdb.confignode.rpc.thrift.TShowPipeReq; +import org.apache.iotdb.confignode.rpc.thrift.TShowPipeResp; import org.apache.iotdb.confignode.rpc.thrift.TShowRegionReq; import org.apache.iotdb.confignode.rpc.thrift.TShowRegionResp; import org.apache.iotdb.confignode.rpc.thrift.TShowSubscriptionReq; @@ -232,6 +235,8 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DeleteDeviceTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DescribeTableDetailsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DescribeTableTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateDatabaseTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreatePipeTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateViewTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowDBTask; @@ -2740,6 +2745,65 @@ public SettableFuture showPipes( return future; } + @Override + public SettableFuture showCreatePipe( + final String pipeName, final String userName) { + final SettableFuture future = SettableFuture.create(); + try (final ConfigNodeClient configNodeClient = + CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { + final TShowPipeReq tShowPipeReq = + new TShowPipeReq().setPipeName(pipeName).setIsTableModel(true); + if (Objects.nonNull(userName)) { + tShowPipeReq.setUserName(userName); + } + final TShowPipeResp showPipeResp = configNodeClient.showPipe(tShowPipeReq); + if (showPipeResp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + future.setException(new IoTDBException(showPipeResp.getStatus())); + return future; + } + if (!showPipeResp.isSetPipeInfoList() || showPipeResp.getPipeInfoList().isEmpty()) { + future.setException( + new IoTDBException( + String.format("Failed to show create pipe %s, the pipe does not exist.", pipeName), + TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode())); + return future; + } + + final TGetAllPipeInfoResp getAllPipeInfoResp = configNodeClient.getAllPipeInfo(); + if (getAllPipeInfoResp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + future.setException( + new IoTDBException( + String.format( + "Failed to get pipe info from config node, status is %s.", + getAllPipeInfoResp.getStatus()), + TSStatusCode.PIPE_ERROR.getStatusCode())); + return future; + } + + final PipeMeta pipeMeta = + getAllPipeInfoResp.getAllPipeInfo().stream() + .map(PipeMeta::deserialize4Coordinator) + .filter( + meta -> + meta.getStaticMeta().visibleUnder(true) + && meta.getStaticMeta().getPipeName().equals(pipeName)) + .findFirst() + .orElse(null); + if (pipeMeta == null) { + future.setException( + new IoTDBException( + String.format("Failed to show create pipe %s, the pipe does not exist.", pipeName), + TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode())); + return future; + } + + ShowCreatePipeTask.buildTsBlock(pipeMeta, future); + } catch (final Exception e) { + future.setException(e); + } + return future; + } + @Override public SettableFuture showSubscriptions( final ShowSubscriptionsStatement showSubscriptionsStatement) { @@ -4140,6 +4204,46 @@ public SettableFuture showDatabases( return future; } + @Override + public SettableFuture showCreateDatabase(final String database) { + final SettableFuture future = SettableFuture.create(); + if (InformationSchema.INFORMATION_DATABASE.equals(database)) { + future.setException( + new IoTDBException( + "The system database does not support show create.", + TSStatusCode.SEMANTIC_ERROR.getStatusCode())); + return future; + } + + final List databasePathPattern = Arrays.asList(ROOT, database); + try (final ConfigNodeClient client = + CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { + final TGetDatabaseReq req = + new TGetDatabaseReq(databasePathPattern, ALL_MATCH_SCOPE.serialize()) + .setIsTableModel(true); + final TShowDatabaseResp resp = client.showDatabase(req); + if (resp.getStatus().getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + future.setException(new IoTDBException(resp.getStatus())); + return future; + } + + final TDatabaseInfo databaseInfo = + resp.isSetDatabaseInfoMap() ? resp.getDatabaseInfoMap().get(database) : null; + if (databaseInfo == null) { + future.setException( + new IoTDBException( + String.format("Unknown database %s", database), + TSStatusCode.DATABASE_NOT_EXIST.getStatusCode())); + return future; + } + + ShowCreateDatabaseTask.buildTsBlock(databaseInfo, future); + } catch (final IOException | ClientManagerException | TException e) { + future.setException(e); + } + return future; + } + @Override public SettableFuture showCluster(final ShowCluster showCluster) { // As the implementation is identical, we'll simply translate to the diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index b4b928ba0b617..3a2c55d686164 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -234,6 +234,8 @@ SettableFuture alterSchemaTemplate( SettableFuture showPipes( ShowPipesStatement showPipesStatement, String userName); + SettableFuture showCreatePipe(String pipeName, String userName); + SettableFuture showSubscriptions( ShowSubscriptionsStatement showSubscriptionsStatement); @@ -333,6 +335,8 @@ SettableFuture showThrottleQuota( SettableFuture showDatabases( final ShowDB showDB, final Predicate canSeenDB); + SettableFuture showCreateDatabase(final String database); + SettableFuture showCluster(ShowCluster showCluster); SettableFuture useDatabase(final Use useDB, final IClientSession clientSession); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java new file mode 100644 index 0000000000000..9fe527a01dda0 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateDatabaseTask.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational; + +import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.schema.column.ColumnHeader; +import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; +import org.apache.iotdb.confignode.rpc.thrift.TDatabaseInfo; +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.IConfigTask; +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.stream.Collectors; + +public class ShowCreateDatabaseTask implements IConfigTask { + + private final String database; + + public ShowCreateDatabaseTask(final String database) { + this.database = database; + } + + @Override + public ListenableFuture execute(final IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.showCreateDatabase(database); + } + + public static void buildTsBlock( + final TDatabaseInfo databaseInfo, final SettableFuture future) { + final List outputDataTypes = + ColumnHeaderConstant.showCreateDatabaseColumnHeaders.stream() + .map(ColumnHeader::getColumnType) + .collect(Collectors.toList()); + + final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); + builder.getTimeColumnBuilder().writeLong(0L); + builder + .getColumnBuilder(0) + .writeBinary(new Binary(databaseInfo.getName(), TSFileConfig.STRING_CHARSET)); + builder + .getColumnBuilder(1) + .writeBinary( + new Binary(getShowCreateDatabaseSQL(databaseInfo), TSFileConfig.STRING_CHARSET)); + builder.declarePosition(); + + final DatasetHeader datasetHeader = DatasetHeaderFactory.getShowCreateDatabaseColumnHeader(); + future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader)); + } + + public static String getShowCreateDatabaseSQL(final TDatabaseInfo databaseInfo) { + return new StringBuilder("CREATE DATABASE ") + .append(ShowCreateTableTask.getIdentifier(databaseInfo.getName())) + .append(" WITH (ttl=") + .append( + databaseInfo.getTTL() == Long.MAX_VALUE + ? ShowCreateTableTask.getString(IoTDBConstant.TTL_INFINITE) + : databaseInfo.getTTL()) + .append(",time_partition_interval=") + .append(databaseInfo.getTimePartitionInterval()) + .append(",schema_region_group_num=") + .append(databaseInfo.getMinSchemaRegionNum()) + .append(",data_region_group_num=") + .append(databaseInfo.getMinDataRegionNum()) + .append(")") + .toString(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java new file mode 100644 index 0000000000000..7f7185e0a79a8 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreatePipeTask.java @@ -0,0 +1,176 @@ +/* + * 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.pipe.agent.task.meta.PipeMeta; +import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant; +import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; +import org.apache.iotdb.commons.schema.column.ColumnHeader; +import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; +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.IConfigTask; +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.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public class ShowCreatePipeTask implements IConfigTask { + + private final String pipeName; + private final String userName; + + public ShowCreatePipeTask(final String pipeName, final String userName) { + this.pipeName = pipeName; + this.userName = userName; + } + + @Override + public ListenableFuture execute(final IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.showCreatePipe(pipeName, userName); + } + + public static void buildTsBlock( + final PipeMeta pipeMeta, final SettableFuture future) { + final List outputDataTypes = + ColumnHeaderConstant.showCreatePipeColumnHeaders.stream() + .map(ColumnHeader::getColumnType) + .collect(Collectors.toList()); + + final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); + builder.getTimeColumnBuilder().writeLong(0L); + builder + .getColumnBuilder(0) + .writeBinary( + new Binary(pipeMeta.getStaticMeta().getPipeName(), TSFileConfig.STRING_CHARSET)); + builder + .getColumnBuilder(1) + .writeBinary(new Binary(getShowCreatePipeSQL(pipeMeta), TSFileConfig.STRING_CHARSET)); + builder.declarePosition(); + + final DatasetHeader datasetHeader = DatasetHeaderFactory.getShowCreatePipeColumnHeader(); + future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader)); + } + + public static String getShowCreatePipeSQL(final PipeMeta pipeMeta) { + final StringBuilder builder = + new StringBuilder("CREATE PIPE ") + .append(ShowCreateTableTask.getIdentifier(pipeMeta.getStaticMeta().getPipeName())); + + appendAttributesClause( + builder, + "WITH SOURCE", + sanitizeSourceAttributes(pipeMeta.getStaticMeta().getSourceParameters().getAttribute())); + appendAttributesClause( + builder, + "WITH PROCESSOR", + sanitizeCommonAttributes(pipeMeta.getStaticMeta().getProcessorParameters().getAttribute())); + appendAttributesClause( + builder, + "WITH SINK", + sanitizeSinkAttributes(pipeMeta.getStaticMeta().getSinkParameters().getAttribute())); + + return builder.toString(); + } + + private static Map sanitizeCommonAttributes( + final Map attributes) { + final Map result = new TreeMap<>(attributes); + result + .entrySet() + .removeIf(entry -> entry.getKey().startsWith(SystemConstant.SYSTEM_PREFIX_KEY)); + result.entrySet().removeIf(entry -> entry.getKey().startsWith(SystemConstant.AUDIT_PREFIX_KEY)); + return result; + } + + private static Map sanitizeSourceAttributes(final Map source) { + final Map result = sanitizeCommonAttributes(source); + result.remove(PipeSourceConstant.EXTRACTOR_IOTDB_USER_ID); + result.remove(PipeSourceConstant.SOURCE_IOTDB_USER_ID); + result.remove(PipeSourceConstant.EXTRACTOR_IOTDB_CLI_HOSTNAME); + result.remove(PipeSourceConstant.SOURCE_IOTDB_CLI_HOSTNAME); + if (!hasAnyKey( + result, + PipeSourceConstant.EXTRACTOR_IOTDB_PASSWORD_KEY, + PipeSourceConstant.SOURCE_IOTDB_PASSWORD_KEY)) { + result.remove(PipeSourceConstant.EXTRACTOR_IOTDB_USER_KEY); + result.remove(PipeSourceConstant.SOURCE_IOTDB_USER_KEY); + result.remove(PipeSourceConstant.EXTRACTOR_IOTDB_USERNAME_KEY); + result.remove(PipeSourceConstant.SOURCE_IOTDB_USERNAME_KEY); + } + return result; + } + + private static Map sanitizeSinkAttributes(final Map sink) { + final Map result = sanitizeCommonAttributes(sink); + result.remove(PipeSinkConstant.CONNECTOR_IOTDB_USER_ID); + result.remove(PipeSinkConstant.SINK_IOTDB_USER_ID); + result.remove(PipeSinkConstant.CONNECTOR_IOTDB_CLI_HOSTNAME); + result.remove(PipeSinkConstant.SINK_IOTDB_CLI_HOSTNAME); + if (!hasAnyKey( + result, + PipeSinkConstant.CONNECTOR_IOTDB_PASSWORD_KEY, + PipeSinkConstant.SINK_IOTDB_PASSWORD_KEY)) { + result.remove(PipeSinkConstant.CONNECTOR_IOTDB_USER_KEY); + result.remove(PipeSinkConstant.SINK_IOTDB_USER_KEY); + result.remove(PipeSinkConstant.CONNECTOR_IOTDB_USERNAME_KEY); + result.remove(PipeSinkConstant.SINK_IOTDB_USERNAME_KEY); + } + return result; + } + + private static void appendAttributesClause( + final StringBuilder builder, final String clause, final Map attributes) { + if (attributes.isEmpty()) { + return; + } + final List pairs = new ArrayList<>(attributes.size()); + for (final Map.Entry entry : attributes.entrySet()) { + pairs.add( + ShowCreateTableTask.getString(entry.getKey()) + + "=" + + ShowCreateTableTask.getString(entry.getValue())); + } + builder.append(" ").append(clause).append(" (").append(String.join(",", pairs)).append(")"); + } + + private static boolean hasAnyKey(final Map attributes, final String... keys) { + for (final String key : keys) { + if (attributes.containsKey(key)) { + return true; + } + } + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index d0779ee24a5b8..17cd05b8bbb4e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -184,6 +184,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; @@ -448,6 +450,11 @@ public Scope visitShowDB(ShowDB node, Optional context) { DataNodeQueryMessages.SHOW_DATABASE_STATEMENT_IS_NOT_SUPPORTED_YET); } + @Override + public Scope visitShowCreateDatabase(ShowCreateDatabase node, Optional context) { + return createAndAssignScope(node, context); + } + @Override public Scope visitCreateTable(final CreateTable node, final Optional context) { validateProperties(node.getProperties(), context); @@ -4817,6 +4824,11 @@ public Scope visitShowPipes(ShowPipes node, Optional context) { return createAndAssignScope(node, context); } + @Override + public Scope visitShowCreatePipe(ShowCreatePipe node, Optional context) { + return createAndAssignScope(node, context); + } + @Override public Scope visitCreatePipePlugin(CreatePipePlugin node, Optional context) { return createAndAssignScope(node, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java index 2e236e116e85f..721323ee2d80f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java @@ -79,6 +79,10 @@ default R visitShowDB(final ShowDB node, final C context) { return visitStatement(node, context); } + default R visitShowCreateDatabase(final ShowCreateDatabase node, final C context) { + return visitStatement(node, context); + } + default R visitCreateTable(final CreateTable node, final C context) { return visitStatement(node, context); } @@ -315,6 +319,10 @@ default R visitShowPipes(ShowPipes node, C context) { return visitStatement(node, context); } + default R visitShowCreatePipe(ShowCreatePipe node, C context) { + return visitStatement(node, context); + } + default R visitCreatePipePlugin(CreatePipePlugin node, C context) { return visitStatement(node, context); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java new file mode 100644 index 0000000000000..330a7145faaec --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateDatabase.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.AstMemoryEstimationHelper; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Node; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.NodeLocation; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class ShowCreateDatabase extends Statement { + + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(ShowCreateDatabase.class); + + private final String database; + + public ShowCreateDatabase(final NodeLocation location, final String database) { + super(requireNonNull(location, "location is null")); + this.database = requireNonNull(database, "database is null").toLowerCase(Locale.ENGLISH); + } + + public String getDatabase() { + return database; + } + + @Override + public R accept(final IAstVisitor visitor, final C context) { + return ((AstVisitor) visitor).visitShowCreateDatabase(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public int hashCode() { + return Objects.hash(database); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final ShowCreateDatabase that = (ShowCreateDatabase) obj; + return Objects.equals(database, that.database); + } + + @Override + public String toString() { + return "SHOW CREATE DATABASE " + database; + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(database); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java new file mode 100644 index 0000000000000..a9a6c4cbe598a --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreatePipe.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.AstMemoryEstimationHelper; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; + +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class ShowCreatePipe extends PipeStatement { + + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(ShowCreatePipe.class); + + private final String pipeName; + + public ShowCreatePipe(final String pipeName) { + this.pipeName = requireNonNull(pipeName, "pipeName is null"); + } + + public String getPipeName() { + return pipeName; + } + + @Override + public R accept(final IAstVisitor visitor, final C context) { + return ((AstVisitor) visitor).visitShowCreatePipe(this, context); + } + + @Override + public int hashCode() { + return Objects.hash(pipeName); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final ShowCreatePipe that = (ShowCreatePipe) obj; + return Objects.equals(pipeName, that.pipeName); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("statement", "SHOW CREATE PIPE") + .add("pipeName", pipeName) + .toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(pipeName); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 768a05689e8f5..f83f54ac65dfe 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -224,6 +224,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfigNodes; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfiguration; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -406,6 +408,13 @@ public Node visitShowDatabasesStatement( return new ShowDB(getLocation(ctx), Objects.nonNull(ctx.DETAILS())); } + @Override + public Node visitShowCreateDatabaseStatement( + final RelationalSqlParser.ShowCreateDatabaseStatementContext ctx) { + return new ShowCreateDatabase( + getLocation(ctx), lowerIdentifier((Identifier) visit(ctx.database)).getValue()); + } + @Override public Node visitCreateDbStatement(final RelationalSqlParser.CreateDbStatementContext ctx) { List properties = ImmutableList.of(); @@ -1332,6 +1341,11 @@ public Node visitShowPipesStatement(RelationalSqlParser.ShowPipesStatementContex return new ShowPipes(pipeName, hasWhereClause); } + @Override + public Node visitShowCreatePipeStatement(RelationalSqlParser.ShowCreatePipeStatementContext ctx) { + return new ShowCreatePipe(((Identifier) visit(ctx.pipeName)).getValue()); + } + @Override public Node visitCreatePipePluginStatement( RelationalSqlParser.CreatePipePluginStatementContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java index 4797d43cf21af..b036ea2991522 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java @@ -26,6 +26,7 @@ import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Table; import org.apache.iotdb.commons.queryengine.plan.relational.sql.util.CommonQuerySqlFormatter; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe; @@ -60,6 +61,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetTableComment; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -138,6 +141,14 @@ public Void visitShowDB(ShowDB node, Integer indent) { return null; } + @Override + public Void visitShowCreateDatabase(ShowCreateDatabase node, Integer indent) { + builder + .append("SHOW CREATE DATABASE ") + .append(ShowCreateTableTask.getIdentifier(node.getDatabase())); + return null; + } + @Override public Void visitShowTables(ShowTables node, Integer indent) { builder.append("SHOW TABLES"); @@ -697,6 +708,14 @@ public Void visitShowPipes(ShowPipes node, Integer context) { return null; } + @Override + public Void visitShowCreatePipe(ShowCreatePipe node, Integer context) { + builder + .append("SHOW CREATE PIPE ") + .append(ShowCreateTableTask.getIdentifier(node.getPipeName())); + return null; + } + @Override public Void visitCreatePipePlugin(CreatePipePlugin node, Integer context) { builder.append("CREATE PIPEPLUGIN "); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java new file mode 100644 index 0000000000000..dfa60953c9d26 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational; + +import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; +import org.apache.iotdb.commons.pipe.config.constant.PipeProcessorConstant; +import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant; +import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; +import org.apache.iotdb.confignode.rpc.thrift.TDatabaseInfo; + +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class ShowCreateTaskTest { + + @Test + public void testShowCreateDatabaseSQL() { + final TDatabaseInfo databaseInfo = new TDatabaseInfo(); + databaseInfo.setName("test_db"); + databaseInfo.setTTL(Long.MAX_VALUE); + databaseInfo.setTimePartitionInterval(604800000L); + databaseInfo.setMinSchemaRegionNum(0); + databaseInfo.setMinDataRegionNum(1); + + assertEquals( + "CREATE DATABASE \"test_db\" WITH (ttl='" + + IoTDBConstant.TTL_INFINITE + + "',time_partition_interval=604800000,schema_region_group_num=0,data_region_group_num=1)", + ShowCreateDatabaseTask.getShowCreateDatabaseSQL(databaseInfo)); + } + + @Test + public void testShowCreatePipeSQLShouldSanitizeInternalAndInjectedAttributes() { + final Map sourceAttributes = new HashMap<>(); + sourceAttributes.put(PipeSourceConstant.SOURCE_KEY, "iotdb-source"); + sourceAttributes.put(SystemConstant.SQL_DIALECT_KEY, SystemConstant.SQL_DIALECT_TABLE_VALUE); + sourceAttributes.put("__audit.source", "audit"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_USER_ID, "1"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_USERNAME_KEY, "alice"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_CLI_HOSTNAME, "host"); + + final Map processorAttributes = new HashMap<>(); + processorAttributes.put(PipeProcessorConstant.PROCESSOR_KEY, "do-nothing-processor"); + processorAttributes.put(SystemConstant.RESTART_OR_NEWLY_ADDED_KEY, "true"); + processorAttributes.put("__audit.processor", "audit"); + + final Map sinkAttributes = new HashMap<>(); + sinkAttributes.put(PipeSinkConstant.SINK_KEY, "write-back-sink"); + sinkAttributes.put(SystemConstant.RESTART_OR_NEWLY_ADDED_KEY, "true"); + sinkAttributes.put("__audit.sink", "audit"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_USER_ID, "1"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_USERNAME_KEY, "alice"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_CLI_HOSTNAME, "host"); + + final PipeMeta pipeMeta = + new PipeMeta( + new PipeStaticMeta( + "test_pipe", 1L, sourceAttributes, processorAttributes, sinkAttributes), + new PipeRuntimeMeta()); + + assertEquals( + "CREATE PIPE \"test_pipe\" WITH SOURCE ('source'='iotdb-source')" + + " WITH PROCESSOR ('processor'='do-nothing-processor')" + + " WITH SINK ('sink'='write-back-sink')", + ShowCreatePipeTask.getShowCreatePipeSQL(pipeMeta)); + } + + @Test + public void testShowCreatePipeSQLShouldKeepExplicitCredentials() { + final Map sourceAttributes = new HashMap<>(); + sourceAttributes.put(PipeSourceConstant.SOURCE_KEY, "iotdb-source"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_USERNAME_KEY, "alice"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_PASSWORD_KEY, "secret"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_USER_ID, "1"); + sourceAttributes.put(PipeSourceConstant.SOURCE_IOTDB_CLI_HOSTNAME, "host"); + + final Map sinkAttributes = new HashMap<>(); + sinkAttributes.put(PipeSinkConstant.SINK_KEY, "write-back-sink"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_USERNAME_KEY, "alice"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_PASSWORD_KEY, "secret"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_USER_ID, "1"); + sinkAttributes.put(PipeSinkConstant.SINK_IOTDB_CLI_HOSTNAME, "host"); + + final PipeMeta pipeMeta = + new PipeMeta( + new PipeStaticMeta("test_pipe", 1L, sourceAttributes, new HashMap<>(), sinkAttributes), + new PipeRuntimeMeta()); + + assertEquals( + "CREATE PIPE \"test_pipe\"" + + " WITH SOURCE ('source'='iotdb-source','source.password'='secret','source.username'='alice')" + + " WITH SINK ('sink'='write-back-sink','sink.password'='secret','sink.username'='alice')", + ShowCreatePipeTask.getShowCreatePipeSQL(pipeMeta)); + } + + @Test + public void testShowCreatePipeSQLShouldSanitizeExtractorAndConnectorAliases() { + final Map sourceAttributes = new HashMap<>(); + sourceAttributes.put(PipeSourceConstant.EXTRACTOR_KEY, "iotdb-extractor"); + sourceAttributes.put(PipeSourceConstant.EXTRACTOR_IOTDB_USER_ID, "1"); + sourceAttributes.put(PipeSourceConstant.EXTRACTOR_IOTDB_USERNAME_KEY, "alice"); + sourceAttributes.put(PipeSourceConstant.EXTRACTOR_IOTDB_CLI_HOSTNAME, "host"); + + final Map sinkAttributes = new HashMap<>(); + sinkAttributes.put(PipeSinkConstant.CONNECTOR_KEY, "iotdb-thrift-connector"); + sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_USER_ID, "1"); + sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_USERNAME_KEY, "alice"); + sinkAttributes.put(PipeSinkConstant.CONNECTOR_IOTDB_CLI_HOSTNAME, "host"); + + final PipeMeta pipeMeta = + new PipeMeta( + new PipeStaticMeta("test_pipe", 1L, sourceAttributes, new HashMap<>(), sinkAttributes), + new PipeRuntimeMeta()); + + assertEquals( + "CREATE PIPE \"test_pipe\"" + + " WITH SOURCE ('extractor'='iotdb-extractor')" + + " WITH SINK ('connector'='iotdb-thrift-connector')", + ShowCreatePipeTask.getShowCreatePipeSQL(pipeMeta)); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index 186f7daa6846d..0cd04f2585f76 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -340,6 +340,9 @@ private ColumnHeaderConstant() { public static final String CREATE_VIEW = "Create View"; public static final String TABLE = "Table"; public static final String CREATE_TABLE = "Create Table"; + public static final String PIPE = "Pipe"; + public static final String CREATE_PIPE = "Create Pipe"; + public static final String CREATE_DATABASE = "Create Database"; public static final String GRANT_OPTION = "GrantOption"; @@ -755,6 +758,15 @@ private ColumnHeaderConstant() { new ColumnHeader(TABLE, TSDataType.TEXT), new ColumnHeader(CREATE_TABLE, TSDataType.TEXT)); + public static final List showCreatePipeColumnHeaders = + ImmutableList.of( + new ColumnHeader(PIPE, TSDataType.TEXT), new ColumnHeader(CREATE_PIPE, TSDataType.TEXT)); + + public static final List showCreateDatabaseColumnHeaders = + ImmutableList.of( + new ColumnHeader(DATABASE, TSDataType.TEXT), + new ColumnHeader(CREATE_DATABASE, TSDataType.TEXT)); + public static final List LIST_USER_COLUMN_HEADERS = ImmutableList.of( new ColumnHeader(USER_ID, TSDataType.INT64), new ColumnHeader(USER, TSDataType.TEXT)); diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index ca76646468677..9223bb6ae3391 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -49,6 +49,7 @@ statement // Database Statement | useDatabaseStatement | showDatabasesStatement + | showCreateDatabaseStatement | createDbStatement | alterDbStatement | dropDbStatement @@ -101,6 +102,7 @@ statement | startPipeStatement | stopPipeStatement | showPipesStatement + | showCreatePipeStatement | createPipePluginStatement | dropPipePluginStatement | showPipePluginsStatement @@ -203,6 +205,10 @@ showDatabasesStatement : SHOW DATABASES (DETAILS)? ; +showCreateDatabaseStatement + : SHOW CREATE DATABASE database=identifier + ; + createDbStatement : CREATE DATABASE (IF NOT EXISTS)? database=identifier (WITH properties)? ; @@ -514,6 +520,10 @@ showPipesStatement : SHOW ((PIPE pipeName=identifier) | PIPES (WHERE (CONNECTOR | SINK) USED BY pipeName=identifier)?) ; +showCreatePipeStatement + : SHOW CREATE PIPE pipeName=identifier + ; + createPipePluginStatement : CREATE PIPEPLUGIN (IF NOT EXISTS)? pluginName=identifier AS className=string uriClause ; From 5daceda752ada70adf6d17d1d9de60552ac40f54 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Mon, 18 May 2026 15:07:02 +0800 Subject: [PATCH 2/2] show create --- .../tablemodel/IoTDBSubscriptionTopicIT.java | 26 ++++ .../apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 | 6 +- .../common/header/DatasetHeaderFactory.java | 4 + .../config/TableConfigTaskVisitor.java | 9 ++ .../config/TreeConfigTaskVisitor.java | 8 ++ .../executor/ClusterConfigTaskExecutor.java | 46 ++++++ .../config/executor/IConfigTaskExecutor.java | 4 + .../sys/subscription/ShowCreateTopicTask.java | 131 ++++++++++++++++++ .../queryengine/plan/parser/ASTVisitor.java | 15 ++ .../analyzer/StatementAnalyzer.java | 6 + .../plan/relational/sql/ast/AstVisitor.java | 4 + .../relational/sql/ast/ShowCreateTopic.java | 84 +++++++++++ .../relational/sql/parser/AstBuilder.java | 7 + .../sql/util/DataNodeSqlFormatter.java | 10 ++ .../plan/statement/StatementType.java | 1 + .../plan/statement/StatementVisitor.java | 5 + .../ShowCreateTopicStatement.java | 72 ++++++++++ .../relational/ShowCreateTaskTest.java | 49 +++++++ .../plan/parser/StatementGeneratorTest.java | 22 +++ .../relational/sql/ShowCreateTopicTest.java | 73 ++++++++++ .../schema/column/ColumnHeaderConstant.java | 7 + .../relational/grammar/sql/RelationalSql.g4 | 5 + 22 files changed, 593 insertions(+), 1 deletion(-) create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowCreateTopicStatement.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ShowCreateTopicTest.java diff --git a/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/tablemodel/IoTDBSubscriptionTopicIT.java b/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/tablemodel/IoTDBSubscriptionTopicIT.java index f3c0dd29f461e..8cf9c7b25bd65 100644 --- a/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/tablemodel/IoTDBSubscriptionTopicIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/subscription/it/dual/tablemodel/IoTDBSubscriptionTopicIT.java @@ -28,6 +28,7 @@ import org.apache.iotdb.isession.ITableSession; import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.MultiClusterIT2SubscriptionTableArchVerification; +import org.apache.iotdb.itbase.env.BaseEnv; import org.apache.iotdb.pipe.it.dual.tablemodel.TableModelUtils; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.subscription.config.TopicConstant; @@ -52,6 +53,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.sql.Connection; +import java.sql.Statement; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Properties; @@ -97,6 +101,28 @@ protected void setUpConfig() { .setIsPipeEnableMemoryCheck(false); } + @Test + public void testShowCreateTopic() throws Exception { + TableModelUtils.createDataBaseAndTable( + senderEnv, "test_show_create_topic_db", "test_show_create_topic_table"); + + try (final Connection connection = senderEnv.getConnection(BaseEnv.TABLE_SQL_DIALECT); + final Statement statement = connection.createStatement()) { + statement.execute( + "create topic test_show_create_topic with ('database'='test_show_create_topic_db','table'='test_show_create_topic_table','format'='" + + TopicConstant.FORMAT_TS_FILE_VALUE + + "')"); + + TestUtils.assertResultSetEqual( + statement.executeQuery("show create topic test_show_create_topic"), + "Topic,Create Topic,", + Collections.singleton( + "test_show_create_topic,CREATE TOPIC \"test_show_create_topic\" WITH ('database'='test_show_create_topic_db','format'='" + + TopicConstant.FORMAT_TS_FILE_VALUE + + "','table'='test_show_create_topic_table'),")); + } + } + @Ignore @Test public void testTabletTopicWithPath() throws Exception { diff --git a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 index c6271c134cb00..f050ce56d437d 100644 --- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 +++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/IoTDBSqlParser.g4 @@ -59,7 +59,7 @@ ddlStatement // Pipe Plugin | createPipePlugin | dropPipePlugin | showPipePlugins // Subscription - | createTopic | dropTopic | showTopics | showSubscriptions | dropSubscription + | createTopic | dropTopic | showTopics | showCreateTopic | showSubscriptions | dropSubscription // CQ | createContinuousQuery | dropContinuousQuery | showContinuousQueries // Cluster @@ -734,6 +734,10 @@ showTopics : SHOW ((TOPIC topicName=identifier) | TOPICS ) ; +showCreateTopic + : SHOW CREATE TOPIC topicName=identifier + ; + showSubscriptions : SHOW SUBSCRIPTIONS (ON topicName=identifier)? ; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java index b2c327b866aa7..c2a1a9f02d191 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/header/DatasetHeaderFactory.java @@ -267,6 +267,10 @@ public static DatasetHeader getShowCreatePipeColumnHeader() { return new DatasetHeader(ColumnHeaderConstant.showCreatePipeColumnHeaders, true); } + public static DatasetHeader getShowCreateTopicColumnHeader() { + return new DatasetHeader(ColumnHeaderConstant.showCreateTopicColumnHeaders, true); + } + public static DatasetHeader getShowCreateDatabaseColumnHeader() { return new DatasetHeader(ColumnHeaderConstant.showCreateDatabaseColumnHeaders, true); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index 4400b039d9a67..7e589cae93ca2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -144,6 +144,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.CreateTopicTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.DropSubscriptionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.DropTopicTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowCreateTopicTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowSubscriptionsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowTopicsTask; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analyzer; @@ -213,6 +214,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfiguration; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -1360,6 +1362,13 @@ public IConfigTask visitShowCreatePipe(ShowCreatePipe node, MPPQueryContext cont return new ShowCreatePipeTask(node.getPipeName(), context.getSession().getUserName()); } + @Override + public IConfigTask visitShowCreateTopic(ShowCreateTopic node, MPPQueryContext context) { + context.setQueryType(QueryType.READ); + accessControl.checkUserGlobalSysPrivilege(context); + return new ShowCreateTopicTask(node); + } + @Override public IConfigTask visitCreatePipePlugin(CreatePipePlugin node, MPPQueryContext context) { context.setQueryType(QueryType.OTHER); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java index 8b7792bca985a..e1c9cf0d414b6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TreeConfigTaskVisitor.java @@ -126,6 +126,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.CreateTopicTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.DropSubscriptionTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.DropTopicTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowCreateTopicTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowSubscriptionsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowTopicsTask; import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; @@ -195,6 +196,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.AlterSchemaTemplateStatement; @@ -742,6 +744,12 @@ public IConfigTask visitShowTopics( return new ShowTopicsTask(showTopicsStatement); } + @Override + public IConfigTask visitShowCreateTopic( + ShowCreateTopicStatement showCreateTopicStatement, MPPQueryContext context) { + return new ShowCreateTopicTask(showCreateTopicStatement); + } + @Override public IConfigTask visitShowSubscriptions( ShowSubscriptionsStatement showSubscriptionsStatement, MPPQueryContext context) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 274eb5682334b..d1370838be7e9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -138,6 +138,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TExtendRegionReq; import org.apache.iotdb.confignode.rpc.thrift.TFetchTableResp; import org.apache.iotdb.confignode.rpc.thrift.TGetAllPipeInfoResp; +import org.apache.iotdb.confignode.rpc.thrift.TGetAllTopicInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TGetDatabaseReq; import org.apache.iotdb.confignode.rpc.thrift.TGetPipePluginTableResp; import org.apache.iotdb.confignode.rpc.thrift.TGetRegionIdReq; @@ -255,6 +256,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.sys.pipe.ShowPipeTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.quota.ShowSpaceQuotaTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.quota.ShowThrottleQuotaTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowCreateTopicTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowSubscriptionsTask; import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowTopicsTask; import org.apache.iotdb.db.queryengine.plan.expression.Expression; @@ -297,6 +299,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.AlterSchemaTemplateStatement; @@ -3002,6 +3005,49 @@ public SettableFuture showTopics( return future; } + @Override + public SettableFuture showCreateTopic( + final ShowCreateTopicStatement showCreateTopicStatement) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; + } + + final SettableFuture future = SettableFuture.create(); + try (final ConfigNodeClient configNodeClient = + CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { + final TGetAllTopicInfoResp getAllTopicInfoResp = configNodeClient.getAllTopicInfo(); + if (getAllTopicInfoResp.getStatus().getCode() + != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + future.setException(new IoTDBException(getAllTopicInfoResp.getStatus())); + return future; + } + + final TopicMeta topicMeta = + getAllTopicInfoResp.getAllTopicInfo().stream() + .map(TopicMeta::deserialize) + .filter( + meta -> + meta.visibleUnder(showCreateTopicStatement.isTableModel()) + && meta.getTopicName().equals(showCreateTopicStatement.getTopicName())) + .findFirst() + .orElse(null); + if (topicMeta == null) { + future.setException( + new IoTDBException( + String.format( + "Failed to show create topic %s, the topic does not exist.", + showCreateTopicStatement.getTopicName()), + TSStatusCode.TOPIC_NOT_EXIST_ERROR.getStatusCode())); + return future; + } + + ShowCreateTopicTask.buildTsBlock(topicMeta, showCreateTopicStatement.isTableModel(), future); + } catch (final Exception e) { + future.setException(e); + } + return future; + } + @Override public SettableFuture alterEncodingCompressor( final String queryId, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index 3a2c55d686164..6c360a098f00a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -78,6 +78,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.AlterSchemaTemplateStatement; @@ -248,6 +249,9 @@ SettableFuture dropSubscription( SettableFuture showTopics(ShowTopicsStatement showTopicsStatement); + SettableFuture showCreateTopic( + ShowCreateTopicStatement showCreateTopicStatement); + SettableFuture alterEncodingCompressor( String queryId, AlterEncodingCompressorStatement alterEncodingCompressorStatement); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java new file mode 100644 index 0000000000000..ae0c8e095cc11 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/sys/subscription/ShowCreateTopicTask.java @@ -0,0 +1,131 @@ +/* + * 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.sys.subscription; + +import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; +import org.apache.iotdb.commons.schema.column.ColumnHeader; +import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; +import org.apache.iotdb.commons.subscription.meta.topic.TopicMeta; +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.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowCreateTableTask; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; +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.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.stream.Collectors; + +public class ShowCreateTopicTask implements IConfigTask { + + private final ShowCreateTopicStatement showCreateTopicStatement; + + public ShowCreateTopicTask(final ShowCreateTopicStatement showCreateTopicStatement) { + this.showCreateTopicStatement = showCreateTopicStatement; + } + + public ShowCreateTopicTask(final ShowCreateTopic showCreateTopic) { + this.showCreateTopicStatement = new ShowCreateTopicStatement(); + this.showCreateTopicStatement.setTopicName(showCreateTopic.getTopicName()); + this.showCreateTopicStatement.setTableModel(true); + } + + @Override + public ListenableFuture execute(final IConfigTaskExecutor configTaskExecutor) + throws InterruptedException { + return configTaskExecutor.showCreateTopic(showCreateTopicStatement); + } + + public static void buildTsBlock( + final TopicMeta topicMeta, + final boolean isTableModel, + final SettableFuture future) { + final List outputDataTypes = + ColumnHeaderConstant.showCreateTopicColumnHeaders.stream() + .map(ColumnHeader::getColumnType) + .collect(Collectors.toList()); + + final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); + builder.getTimeColumnBuilder().writeLong(0L); + builder + .getColumnBuilder(0) + .writeBinary(new Binary(topicMeta.getTopicName(), TSFileConfig.STRING_CHARSET)); + builder + .getColumnBuilder(1) + .writeBinary( + new Binary( + getShowCreateTopicSQL(topicMeta, isTableModel), TSFileConfig.STRING_CHARSET)); + builder.declarePosition(); + + final DatasetHeader datasetHeader = DatasetHeaderFactory.getShowCreateTopicColumnHeader(); + future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS, builder.build(), datasetHeader)); + } + + public static String getShowCreateTopicSQL( + final TopicMeta topicMeta, final boolean isTableModel) { + final StringBuilder builder = + new StringBuilder("CREATE TOPIC ") + .append(getTopicIdentifier(topicMeta.getTopicName(), isTableModel)); + + final Map sanitizedAttributes = + sanitizeTopicAttributes(topicMeta.getConfig().getAttribute()); + if (!sanitizedAttributes.isEmpty()) { + final List pairs = new ArrayList<>(sanitizedAttributes.size()); + for (final Map.Entry entry : sanitizedAttributes.entrySet()) { + pairs.add( + ShowCreateTableTask.getString(entry.getKey()) + + "=" + + ShowCreateTableTask.getString(entry.getValue())); + } + builder.append(" WITH (").append(String.join(",", pairs)).append(")"); + } + + return builder.toString(); + } + + private static String getTopicIdentifier(final String topicName, final boolean isTableModel) { + if (isTableModel) { + return ShowCreateTableTask.getIdentifier(topicName); + } + return "`" + topicName.replace("`", "``") + "`"; + } + + private static Map sanitizeTopicAttributes(final Map attributes) { + final Map result = new TreeMap<>(attributes); + result + .entrySet() + .removeIf(entry -> entry.getKey().startsWith(SystemConstant.SYSTEM_PREFIX_KEY)); + result.entrySet().removeIf(entry -> entry.getKey().startsWith(SystemConstant.AUDIT_PREFIX_KEY)); + return result; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index 4a82438b5deec..42e6237dd4b1f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -217,6 +217,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ActivateTemplateStatement; @@ -4463,6 +4464,20 @@ public Statement visitShowTopics(IoTDBSqlParser.ShowTopicsContext ctx) { return showTopicsStatement; } + @Override + public Statement visitShowCreateTopic(IoTDBSqlParser.ShowCreateTopicContext ctx) { + final ShowCreateTopicStatement showCreateTopicStatement = new ShowCreateTopicStatement(); + + if (ctx.topicName != null) { + showCreateTopicStatement.setTopicName(parseIdentifier(ctx.topicName.getText())); + } else { + throw new SemanticException( + "Not support for this sql in SHOW CREATE TOPIC, please enter topicName."); + } + + return showCreateTopicStatement; + } + @Override public Statement visitShowSubscriptions(IoTDBSqlParser.ShowSubscriptionsContext ctx) { final ShowSubscriptionsStatement showSubscriptionsStatement = new ShowSubscriptionsStatement(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index 17cd05b8bbb4e..247a324ff3653 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -186,6 +186,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowDevice; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowFunctions; @@ -4859,6 +4860,11 @@ public Scope visitShowTopics(ShowTopics node, Optional context) { return createAndAssignScope(node, context); } + @Override + public Scope visitShowCreateTopic(ShowCreateTopic node, Optional context) { + return createAndAssignScope(node, context); + } + @Override public Scope visitShowSubscriptions(ShowSubscriptions node, Optional context) { return createAndAssignScope(node, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java index 721323ee2d80f..43ed5ac7022de 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java @@ -355,6 +355,10 @@ default R visitShowTopics(ShowTopics node, C context) { return visitStatement(node, context); } + default R visitShowCreateTopic(ShowCreateTopic node, C context) { + return visitStatement(node, context); + } + default R visitShowSubscriptions(ShowSubscriptions node, C context) { return visitStatement(node, context); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java new file mode 100644 index 0000000000000..6f2822c7ec41b --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/ShowCreateTopic.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.AstMemoryEstimationHelper; +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.IAstVisitor; + +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.Objects; + +import static com.google.common.base.MoreObjects.toStringHelper; +import static java.util.Objects.requireNonNull; + +public class ShowCreateTopic extends SubscriptionStatement { + + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(ShowCreateTopic.class); + + private final String topicName; + + public ShowCreateTopic(final String topicName) { + this.topicName = requireNonNull(topicName, "topicName is null"); + } + + public String getTopicName() { + return topicName; + } + + @Override + public R accept(final IAstVisitor visitor, final C context) { + return ((AstVisitor) visitor).visitShowCreateTopic(this, context); + } + + @Override + public int hashCode() { + return Objects.hash(topicName); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + final ShowCreateTopic that = (ShowCreateTopic) obj; + return Objects.equals(topicName, that.topicName); + } + + @Override + public String toString() { + return toStringHelper(this) + .add("statement", "SHOW CREATE TOPIC") + .add("topicName", topicName) + .toString(); + } + + @Override + public long ramBytesUsed() { + long size = INSTANCE_SIZE; + size += AstMemoryEstimationHelper.getEstimatedSizeOfNodeLocation(getLocationInternal()); + size += RamUsageEstimator.sizeOf(topicName); + return size; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index f83f54ac65dfe..dac9350bd2645 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -226,6 +226,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowConfiguration; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -1422,6 +1423,12 @@ public Node visitShowTopicsStatement(RelationalSqlParser.ShowTopicsStatementCont return new ShowTopics(topicName); } + @Override + public Node visitShowCreateTopicStatement( + RelationalSqlParser.ShowCreateTopicStatementContext ctx) { + return new ShowCreateTopic(((Identifier) visit(ctx.topicName)).getValue()); + } + @Override public Node visitShowSubscriptionsStatement( RelationalSqlParser.ShowSubscriptionsStatementContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java index b036ea2991522..6090ab2344b19 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/DataNodeSqlFormatter.java @@ -63,6 +63,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowClusterId; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreatePipe; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentDatabase; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCurrentTimestamp; @@ -805,6 +806,15 @@ public Void visitShowTopics(ShowTopics node, Integer context) { return null; } + @Override + public Void visitShowCreateTopic(ShowCreateTopic node, Integer context) { + builder + .append("SHOW CREATE TOPIC ") + .append(ShowCreateTableTask.getIdentifier(node.getTopicName())); + + return null; + } + @Override public Void visitShowSubscriptions(ShowSubscriptions node, Integer context) { if (Objects.isNull(node.getTopicName())) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java index b40c6444816fc..2606162e3213b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementType.java @@ -186,6 +186,7 @@ public enum StatementType { CREATE_TOPIC, DROP_TOPIC, SHOW_TOPICS, + SHOW_CREATE_TOPIC, SHOW_SUBSCRIPTIONS, DROP_SUBSCRIPTION, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java index 847e850c52172..ca161e12c4333 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/StatementVisitor.java @@ -110,6 +110,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.CreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropSubscriptionStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.DropTopicStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowSubscriptionsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowTopicsStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ActivateTemplateStatement; @@ -652,6 +653,10 @@ public R visitShowTopics(ShowTopicsStatement showTopicsStatement, C context) { return visitStatement(showTopicsStatement, context); } + public R visitShowCreateTopic(ShowCreateTopicStatement showCreateTopicStatement, C context) { + return visitStatement(showCreateTopicStatement, context); + } + public R visitShowSubscriptions( ShowSubscriptionsStatement showSubscriptionsStatement, C context) { return visitStatement(showSubscriptionsStatement, context); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowCreateTopicStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowCreateTopicStatement.java new file mode 100644 index 0000000000000..6e2b9bceaafd0 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowCreateTopicStatement.java @@ -0,0 +1,72 @@ +/* + * 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.statement.metadata.subscription; + +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.statement.IConfigStatement; +import org.apache.iotdb.db.queryengine.plan.statement.Statement; +import org.apache.iotdb.db.queryengine.plan.statement.StatementType; +import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor; + +import java.util.Collections; +import java.util.List; + +public class ShowCreateTopicStatement extends Statement implements IConfigStatement { + + private String topicName; + private boolean isTableModel; + + public ShowCreateTopicStatement() { + super(); + statementType = StatementType.SHOW_CREATE_TOPIC; + } + + public String getTopicName() { + return topicName; + } + + public boolean isTableModel() { + return isTableModel; + } + + public void setTopicName(final String topicName) { + this.topicName = topicName; + } + + public void setTableModel(final boolean tableModel) { + this.isTableModel = tableModel; + } + + @Override + public R accept(final StatementVisitor visitor, final C context) { + return visitor.visitShowCreateTopic(this, context); + } + + @Override + public QueryType getQueryType() { + return QueryType.READ; + } + + @Override + public List getPaths() { + return Collections.emptyList(); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java index dfa60953c9d26..2ad114ad17dca 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowCreateTaskTest.java @@ -27,7 +27,9 @@ import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant; import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; +import org.apache.iotdb.commons.subscription.meta.topic.TopicMeta; import org.apache.iotdb.confignode.rpc.thrift.TDatabaseInfo; +import org.apache.iotdb.db.queryengine.plan.execution.config.sys.subscription.ShowCreateTopicTask; import org.junit.Test; @@ -54,6 +56,20 @@ public void testShowCreateDatabaseSQL() { ShowCreateDatabaseTask.getShowCreateDatabaseSQL(databaseInfo)); } + @Test + public void testShowCreateDatabaseSQLShouldQuoteIdentifierAndKeepFiniteTTL() { + final TDatabaseInfo databaseInfo = new TDatabaseInfo(); + databaseInfo.setName("test\"db"); + databaseInfo.setTTL(123L); + databaseInfo.setTimePartitionInterval(1000L); + databaseInfo.setMinSchemaRegionNum(2); + databaseInfo.setMinDataRegionNum(3); + + assertEquals( + "CREATE DATABASE \"test\"\"db\" WITH (ttl=123,time_partition_interval=1000,schema_region_group_num=2,data_region_group_num=3)", + ShowCreateDatabaseTask.getShowCreateDatabaseSQL(databaseInfo)); + } + @Test public void testShowCreatePipeSQLShouldSanitizeInternalAndInjectedAttributes() { final Map sourceAttributes = new HashMap<>(); @@ -143,4 +159,37 @@ public void testShowCreatePipeSQLShouldSanitizeExtractorAndConnectorAliases() { + " WITH SINK ('connector'='iotdb-thrift-connector')", ShowCreatePipeTask.getShowCreatePipeSQL(pipeMeta)); } + + @Test + public void testShowCreateTopicSQLShouldSanitizeInternalAttributes() { + final Map topicAttributes = new HashMap<>(); + topicAttributes.put("table", "test_table"); + topicAttributes.put(SystemConstant.SQL_DIALECT_KEY, SystemConstant.SQL_DIALECT_TABLE_VALUE); + topicAttributes.put("format", "SubscriptionTsFileHandler"); + topicAttributes.put("database", "test_db"); + topicAttributes.put("__audit.topic", "audit"); + + final TopicMeta topicMeta = new TopicMeta("test_topic", 1L, topicAttributes); + + assertEquals( + "CREATE TOPIC \"test_topic\" WITH ('database'='test_db','format'='SubscriptionTsFileHandler','table'='test_table')", + ShowCreateTopicTask.getShowCreateTopicSQL(topicMeta, true)); + assertEquals( + "CREATE TOPIC `test_topic` WITH ('database'='test_db','format'='SubscriptionTsFileHandler','table'='test_table')", + ShowCreateTopicTask.getShowCreateTopicSQL(topicMeta, false)); + } + + @Test + public void testShowCreateTopicSQLShouldOmitEmptyWithClause() { + final Map topicAttributes = new HashMap<>(); + topicAttributes.put(SystemConstant.SQL_DIALECT_KEY, SystemConstant.SQL_DIALECT_TABLE_VALUE); + topicAttributes.put("__audit.topic", "audit"); + + final TopicMeta topicMeta = new TopicMeta("test`topic", 1L, topicAttributes); + + assertEquals( + "CREATE TOPIC \"test`topic\"", ShowCreateTopicTask.getShowCreateTopicSQL(topicMeta, true)); + assertEquals( + "CREATE TOPIC `test``topic`", ShowCreateTopicTask.getShowCreateTopicSQL(topicMeta, false)); + } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java index b98f34a2484d9..6ffe85e6f32c9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java @@ -56,6 +56,7 @@ import org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteDatabaseStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteTimeSeriesStatement; +import org.apache.iotdb.db.queryengine.plan.statement.metadata.subscription.ShowCreateTopicStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.BatchActivateTemplateStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.CreateSchemaTemplateStatement; import org.apache.iotdb.db.queryengine.plan.statement.metadata.template.DropSchemaTemplateStatement; @@ -693,6 +694,27 @@ private AuthorStatement createAuthDclStmt(String sql) { return roleDcl; } + @Test + public void testShowCreateTopicStatement() { + final Statement stmt = + StatementGenerator.createStatement( + "SHOW CREATE TOPIC topic1", ZonedDateTime.now().getOffset()); + + assertTrue(stmt instanceof ShowCreateTopicStatement); + assertEquals(StatementType.SHOW_CREATE_TOPIC, stmt.getType()); + assertEquals("topic1", ((ShowCreateTopicStatement) stmt).getTopicName()); + } + + @Test + public void testShowCreateTopicStatementWithQuotedIdentifier() { + final Statement stmt = + StatementGenerator.createStatement( + "SHOW CREATE TOPIC `topic``1`", ZonedDateTime.now().getOffset()); + + assertTrue(stmt instanceof ShowCreateTopicStatement); + assertEquals("topic`1", ((ShowCreateTopicStatement) stmt).getTopicName()); + } + @Test public void testDCLUserOperation() { AuthorStatement unlockDcl = createAuthDclStmt("ALTER USER test @ '127.0.0.1' ACCOUNT UNLOCK;"); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ShowCreateTopicTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ShowCreateTopicTest.java new file mode 100644 index 0000000000000..835944596dc40 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ShowCreateTopicTest.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql; + +import org.apache.iotdb.commons.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.db.protocol.session.IClientSession; +import org.apache.iotdb.db.protocol.session.InternalClientSession; +import org.apache.iotdb.db.queryengine.common.MPPQueryContext; +import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analysis; +import org.apache.iotdb.db.queryengine.plan.relational.analyzer.AnalyzerTest; +import org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ShowCreateTopic; +import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; +import org.apache.iotdb.db.queryengine.plan.relational.sql.util.DataNodeSqlFormatter; + +import org.junit.Before; +import org.junit.Test; + +import java.time.ZoneId; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class ShowCreateTopicTest { + + private SqlParser sqlParser; + private IClientSession clientSession; + + @Before + public void setUp() { + sqlParser = new SqlParser(); + clientSession = new InternalClientSession("testClient"); + clientSession.setDatabaseName("testdb"); + } + + @Test + public void testShowCreateTopicRoundTripWithQuotedIdentifier() { + final String sql = "SHOW CREATE TOPIC \"topic-1\""; + final Statement statement = + sqlParser.createStatement(sql, ZoneId.systemDefault(), clientSession); + + assertTrue(statement instanceof ShowCreateTopic); + assertEquals("topic-1", ((ShowCreateTopic) statement).getTopicName()); + assertEquals(sql, DataNodeSqlFormatter.formatDataNodeSql(statement)); + + final Analysis analysis = + AnalyzerTest.analyzeSQL( + sql, + TestUtils.TEST_MATADATA, + new MPPQueryContext( + sql, new QueryId("show_create_topic_test"), TestUtils.SESSION_INFO, null, null)); + assertNotNull(analysis); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index 0cd04f2585f76..842a7f18d2580 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -135,6 +135,8 @@ private ColumnHeaderConstant() { // column names for show topics statement public static final String TOPIC_NAME = "TopicName"; public static final String TOPIC_CONFIGS = "TopicConfigs"; + public static final String TOPIC = "Topic"; + public static final String CREATE_TOPIC = "Create Topic"; // column names for show subscriptions statement public static final String CONSUMER_GROUP_NAME = "ConsumerGroupName"; @@ -614,6 +616,11 @@ private ColumnHeaderConstant() { new ColumnHeader(TOPIC_NAME, TSDataType.TEXT), new ColumnHeader(TOPIC_CONFIGS, TSDataType.TEXT)); + public static final List showCreateTopicColumnHeaders = + ImmutableList.of( + new ColumnHeader(TOPIC, TSDataType.TEXT), + new ColumnHeader(CREATE_TOPIC, TSDataType.TEXT)); + public static final List showSubscriptionColumnHeaders = ImmutableList.of( new ColumnHeader(SUBSCRIPTION_ID, TSDataType.TEXT), diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index 9223bb6ae3391..e571485119524 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -111,6 +111,7 @@ statement | createTopicStatement | dropTopicStatement | showTopicsStatement + | showCreateTopicStatement | showSubscriptionsStatement | dropSubscriptionStatement @@ -558,6 +559,10 @@ showTopicsStatement : SHOW ((TOPIC topicName=identifier) | TOPICS ) ; +showCreateTopicStatement + : SHOW CREATE TOPIC topicName=identifier + ; + showSubscriptionsStatement : SHOW SUBSCRIPTIONS (ON topicName=identifier)? ;