diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java index afe56721e54eec..865ce6e243a376 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableCommand.java @@ -212,6 +212,10 @@ public CreateTableInfo getCreateTableInfo() { return createTableInfo; } + public Optional getCtasQuery() { + return ctasQuery; + } + @Override public StmtType stmtType() { return StmtType.CREATE; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableLikeCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableLikeCommand.java index 3b59c31288057b..e02790b5adb97d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableLikeCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/CreateTableLikeCommand.java @@ -19,13 +19,27 @@ import org.apache.doris.analysis.CreateTableLikeStmt; import org.apache.doris.analysis.StmtType; +import org.apache.doris.catalog.DatabaseIf; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.OlapTable; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.nereids.parser.NereidsParser; import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.CreateTableInfo; import org.apache.doris.nereids.trees.plans.commands.info.CreateTableLikeInfo; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.StmtExecutor; +import com.google.common.collect.Lists; +import org.apache.commons.collections.CollectionUtils; + +import java.util.List; + /** CreateTableLikeCommand */ public class CreateTableLikeCommand extends Command implements ForwardWithSync { private final CreateTableLikeInfo info; @@ -40,7 +54,7 @@ public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { executor.checkBlockRules(); info.validate(ctx); CreateTableLikeStmt stmt = info.translateToLegacyStmt(); - Env.getCurrentEnv().createTableLike(stmt); + doRun(stmt, ctx, executor); } @Override @@ -52,4 +66,60 @@ public R accept(PlanVisitor visitor, C context) { public StmtType stmtType() { return StmtType.CREATE; } + + private void doRun(CreateTableLikeStmt stmt, ConnectContext ctx, StmtExecutor executor) throws Exception { + try { + DatabaseIf db = Env.getCurrentInternalCatalog().getDbOrDdlException(stmt.getExistedDbName()); + TableIf table = db.getTableOrDdlException(stmt.getExistedTableName()); + + if (table.getType() == TableIf.TableType.VIEW) { + throw new DdlException("Not support create table from a View"); + } + + List createTableStmt = Lists.newArrayList(); + table.readLock(); + try { + if (table.isManagedTable()) { + if (!CollectionUtils.isEmpty(stmt.getRollupNames())) { + OlapTable olapTable = (OlapTable) table; + for (String rollupIndexName : stmt.getRollupNames()) { + if (!olapTable.hasMaterializedIndex(rollupIndexName)) { + throw new DdlException("Rollup index[" + rollupIndexName + "] not exists in Table[" + + olapTable.getName() + "]"); + } + } + } + } else if (!CollectionUtils.isEmpty(stmt.getRollupNames()) || stmt.isWithAllRollup()) { + throw new DdlException("Table[" + table.getName() + "] is external, not support rollup copy"); + } + + Env.getDdlStmt(stmt, stmt.getDbName(), table, createTableStmt, null, null, false, false, true, -1L, + false, false); + if (createTableStmt.isEmpty()) { + ErrorReport.reportDdlException(ErrorCode.ERROR_CREATE_TABLE_LIKE_EMPTY, "CREATE"); + } + } finally { + table.readUnlock(); + } + + try { + // analyze CreateTableStmt will check create_priv of existedTable, create table like only need + // create_priv of newTable, and select_priv of existedTable, and priv check has done in + // CreateTableStmt/CreateTableCommand, so we skip it + ctx.setSkipAuth(true); + NereidsParser nereidsParser = new NereidsParser(); + CreateTableCommand createTableCommand = (CreateTableCommand) nereidsParser + .parseSingle(createTableStmt.get(0)); + CreateTableInfo createTableInfo = createTableCommand.getCreateTableInfo(); + createTableCommand = new CreateTableCommand(createTableCommand.getCtasQuery(), + createTableInfo.withTableNameAndIfNotExists(stmt.getTableName(), stmt.isIfNotExists())); + createTableCommand.run(ctx, executor); + } finally { + ctx.setSkipAuth(false); + } + } catch (UserException e) { + throw new DdlException("Failed to execute CREATE TABLE LIKE " + stmt.getExistedTableName() + ". Reason: " + + e.getMessage(), e); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java index 9b3a35e1760ff1..b4b8e21800bbae 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/info/CreateTableInfo.java @@ -201,6 +201,21 @@ public CreateTableInfo(boolean ifNotExists, boolean isExternal, String ctlName, this.clusterKeysColumnNames = Utils.copyRequiredList(clusterKeyColumnNames); } + /** + * withTableNameAndIfNotExists + */ + public CreateTableInfo withTableNameAndIfNotExists(String tableName, boolean ifNotExists) { + if (ctasColumns != null) { + return new CreateTableInfo(ifNotExists, isExternal, ctlName, dbName, tableName, ctasColumns, engineName, + keysType, keys, comment, partitionTableInfo, distribution, rollups, properties, extProperties, + clusterKeysColumnNames); + } else { + return new CreateTableInfo(ifNotExists, isExternal, ctlName, dbName, tableName, columns, indexes, + engineName, keysType, keys, comment, partitionTableInfo, distribution, rollups, properties, + extProperties, clusterKeysColumnNames); + } + } + public List getCtasColumns() { return ctasColumns; } diff --git a/regression-test/suites/nereids_p0/create_table/test_create_table_like.groovy b/regression-test/suites/nereids_p0/create_table/test_create_table_like.groovy new file mode 100644 index 00000000000000..f3d0cda01f7bbd --- /dev/null +++ b/regression-test/suites/nereids_p0/create_table/test_create_table_like.groovy @@ -0,0 +1,39 @@ +// 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. + +// this suite is for creating table with timestamp datatype in defferent +// case. For example: 'year' and 'Year' datatype should also be valid in definition + +suite("test_create_table_like") { + multi_sql """ + DROP TABLE IF EXISTS test_create_table_like_t1; + + CREATE TABLE `test_create_table_like_t1` ( + `id` int NULL, + `m_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP + ) ENGINE=OLAP + UNIQUE KEY(`id`) + DISTRIBUTED BY HASH(`id`) BUCKETS AUTO + PROPERTIES ( + "replication_allocation" = "tag.location.default: 1" + ); + + DROP TABLE IF EXISTS test_create_table_like_t2; + + CREATE TABLE test_create_table_like_t2 like test_create_table_like_t1; + """ +}