Code review comment: ```suggestion                         ErrorReport.reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "create denied"); ```

Code under review:
public Table createTable(LocalMetastore metastore, Database db, CreateTableStmt stmt) throws DdlException {
        GlobalStateMgr stateMgr = metastore.getStateMgr();
        EditLog editLog = metastore.getEditLog();
        ColocateTableIndex colocateTableIndex = metastore.getColocateTableIndex();
        String tableName = stmt.getTableName();

        LOG.debug("begin create olap table: {}", tableName);

        
        List<Column> baseSchema = stmt.getColumns();
        metastore.validateColumns(baseSchema);

        
        PartitionDesc partitionDesc = stmt.getPartitionDesc();
        PartitionInfo partitionInfo;
        Map<String, Long> partitionNameToId = Maps.newHashMap();
        if (partitionDesc != null) {
            
            if (partitionDesc instanceof RangePartitionDesc) {
                RangePartitionDesc rangePartitionDesc = (RangePartitionDesc) partitionDesc;
                for (SingleRangePartitionDesc desc : rangePartitionDesc.getSingleRangePartitionDescs()) {
                    long partitionId = metastore.getNextId();
                    partitionNameToId.put(desc.getPartitionName(), partitionId);
                }
            } else if (partitionDesc instanceof ListPartitionDesc) {
                ListPartitionDesc listPartitionDesc = (ListPartitionDesc) partitionDesc;
                listPartitionDesc.findAllPartitionNames()
                        .forEach(partitionName -> partitionNameToId.put(partitionName, metastore.getNextId()));
            } else if (partitionDesc instanceof ExpressionPartitionDesc) {
                ExpressionPartitionDesc expressionPartitionDesc = (ExpressionPartitionDesc) partitionDesc;
                for (SingleRangePartitionDesc desc : expressionPartitionDesc.getRangePartitionDesc()
                        .getSingleRangePartitionDescs()) {
                    long partitionId = metastore.getNextId();
                    partitionNameToId.put(desc.getPartitionName(), partitionId);
                }

                DynamicPartitionUtil.checkIfAutomaticPartitionAllowed(stmt.getProperties());

            } else {
                throw new DdlException("Currently only support range or list partition with engine type olap");
            }
            partitionInfo = partitionDesc.toPartitionInfo(baseSchema, partitionNameToId, false, true);

            
            if (partitionInfo instanceof ExpressionRangePartitionInfo) {
                ExpressionRangePartitionInfo expressionRangePartitionInfo = (ExpressionRangePartitionInfo) partitionInfo;
                long partitionId = metastore.getNextId();
                String replicateNum = stmt.getProperties().get("replication_num");
                expressionRangePartitionInfo.createAutomaticShadowPartition(partitionId, replicateNum);
                partitionNameToId.put(ExpressionRangePartitionInfo.AUTOMATIC_SHADOW_PARTITION_NAME, partitionId);
            }

        } else {
            if (DynamicPartitionUtil.checkDynamicPartitionPropertiesExist(stmt.getProperties())) {
                throw new DdlException("Only support dynamic partition properties on range partition table");
            }
            long partitionId = metastore.getNextId();
            
            partitionNameToId.put(tableName, partitionId);
            partitionInfo = new SinglePartitionInfo();
        }

        
        KeysDesc keysDesc = stmt.getKeysDesc();
        Preconditions.checkNotNull(keysDesc);
        KeysType keysType = keysDesc.getKeysType();

        
        DistributionDesc distributionDesc = stmt.getDistributionDesc();
        Preconditions.checkNotNull(distributionDesc);
        DistributionInfo distributionInfo = distributionDesc.toDistributionInfo(baseSchema);

        short shortKeyColumnCount = 0;
        List<Integer> sortKeyIdxes = new ArrayList<>();
        if (stmt.getSortKeys() != null) {
            List<String> baseSchemaNames = baseSchema.stream().map(Column::getName).collect(Collectors.toList());
            for (String column : stmt.getSortKeys()) {
                int idx = baseSchemaNames.indexOf(column);
                if (idx == -1) {
                    throw new DdlException("Invalid column '" + column + "': not exists in all columns.");
                }
                sortKeyIdxes.add(idx);
            }
            shortKeyColumnCount =
                    GlobalStateMgr.calcShortKeyColumnCount(baseSchema, stmt.getProperties(), sortKeyIdxes);
        } else {
            shortKeyColumnCount = GlobalStateMgr.calcShortKeyColumnCount(baseSchema, stmt.getProperties());
        }
        LOG.debug("create table[{}] short key column count: {}", tableName, shortKeyColumnCount);
        
        TableIndexes indexes = new TableIndexes(stmt.getIndexes());

        
        
        Map<String, String> properties = stmt.getProperties();

        
        long tableId = GlobalStateMgr.getCurrentState().getNextId();
        OlapTable table;
        if (stmt.isExternal()) {
            table = new ExternalOlapTable(db.getId(), tableId, tableName, baseSchema, keysType, partitionInfo,
                    distributionInfo, indexes, properties);
        } else if (stmt.isOlapEngine()) {
            if (distributionInfo.getBucketNum() == 0) {
                int bucketNum = CatalogUtils.calBucketNumAccordingToBackends();
                distributionInfo.setBucketNum(bucketNum);
            }

            RunMode runMode = RunMode.getCurrentRunMode();
            String volume = (properties != null) ? properties.remove(PropertyAnalyzer.PROPERTIES_STORAGE_VOLUME) : null;

            if ("local".equalsIgnoreCase(volume)) {
                table = new OlapTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
            } else if ("default".equalsIgnoreCase(volume)) {
                table = new LakeTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
                metastore.setLakeStorageInfo(table, properties);
            } else if (!Strings.isNullOrEmpty(volume)) {
                throw new DdlException("Unknown storage volume \"" + volume + "\"");
            } else if (runMode == RunMode.SHARED_DATA) {
                table = new LakeTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
                metastore.setLakeStorageInfo(table, properties);
            } else {
                table = new OlapTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
            }

            if (table.isLakeTable() && !runMode.isAllowCreateLakeTable())  {
                throw new DdlException("Cannot create table with persistent volume in current run mode \"" + runMode + "\"");
            }
            if (table.isOlapTable() && !runMode.isAllowCreateOlapTable()) {
                throw new DdlException("Cannot create table without persistent volume in current run mode \"" + runMode + "\"");
            }
        } else {
            throw new DdlException("Unrecognized engine \"" + stmt.getEngineName() + "\"");
        }

        table.setComment(stmt.getComment());

        
        long baseIndexId = metastore.getNextId();
        table.setBaseIndexId(baseIndexId);

        
        Set<String> bfColumns = null;
        double bfFpp = 0;
        try {
            bfColumns = PropertyAnalyzer.analyzeBloomFilterColumns(properties, baseSchema,
                    table.getKeysType() == KeysType.PRIMARY_KEYS);
            if (bfColumns != null && bfColumns.isEmpty()) {
                bfColumns = null;
            }

            bfFpp = PropertyAnalyzer.analyzeBloomFilterFpp(properties);
            if (bfColumns != null && bfFpp == 0) {
                bfFpp = FeConstants.DEFAULT_BLOOM_FILTER_FPP;
            } else if (bfColumns == null) {
                bfFpp = 0;
            }

            table.setBloomFilterInfo(bfColumns, bfFpp);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        short replicationNum = RunMode.defaultReplicationNum();
        try {
            boolean isReplicationNumSet =
                    properties != null && properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM);
            replicationNum = PropertyAnalyzer.analyzeReplicationNum(properties, replicationNum);
            if (isReplicationNumSet) {
                table.setReplicationNum(replicationNum);
            }
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        boolean isInMemory =
                PropertyAnalyzer.analyzeBooleanProp(properties, PropertyAnalyzer.PROPERTIES_INMEMORY, false);
        table.setIsInMemory(isInMemory);

        boolean enablePersistentIndex =
                PropertyAnalyzer.analyzeBooleanProp(properties, PropertyAnalyzer.PROPERTIES_ENABLE_PERSISTENT_INDEX,
                        false);
        table.setEnablePersistentIndex(enablePersistentIndex);

        if (properties != null && (properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_ENABLE) ||
                properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_MAX_SIZE) ||
                properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_TTL))) {
            try {
                boolean enableBinlog = PropertyAnalyzer.analyzeBooleanProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_ENABLE, false);
                long binlogTtl = PropertyAnalyzer.analyzeLongProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_TTL, Config.binlog_ttl_second);
                long binlogMaxSize = PropertyAnalyzer.analyzeLongProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_MAX_SIZE, Config.binlog_max_size);
                BinlogConfig binlogConfig = new BinlogConfig(0, enableBinlog,
                        binlogTtl, binlogMaxSize);
                table.setCurBinlogConfig(binlogConfig);
                LOG.info("create table {} set binlog config, enable_binlog = {}, binlogTtl = {}, binlog_max_size = {}",
                        tableName, enableBinlog, binlogTtl, binlogMaxSize);
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
        }

        
        try {
            table.setWriteQuorum(PropertyAnalyzer.analyzeWriteQuorum(properties));
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        if (table.hasAutoIncrementColumn() && stmt.isOlapEngine()
                && RunMode.allowCreateLakeTable()) {
            throw new DdlException("Table with AUTO_INCREMENT column can not be lake table");
        }

        
        table.setEnableReplicatedStorage(
                PropertyAnalyzer.analyzeBooleanProp(
                        properties, PropertyAnalyzer.PROPERTIES_REPLICATED_STORAGE,
                        Config.enable_replicated_storage_as_default_engine));

        if (table.enableReplicatedStorage().equals(false)) {
            for (Column col : baseSchema) {
                if (col.isAutoIncrement()) {
                    throw new DdlException("Table with AUTO_INCREMENT column must use Replicated Storage");
                }
            }
        }

        TTabletType tabletType = TTabletType.TABLET_TYPE_DISK;
        try {
            tabletType = PropertyAnalyzer.analyzeTabletType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        if (partitionInfo.getType() == PartitionType.UNPARTITIONED) {
            
            

            
            long partitionId = partitionNameToId.get(tableName);
            DataProperty dataProperty = null;
            try {
                boolean hasMedium = false;
                if (properties != null) {
                    hasMedium = properties.containsKey(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM);
                }
                dataProperty = PropertyAnalyzer.analyzeDataProperty(properties,
                        DataProperty.getInferredDefaultDataProperty());
                if (hasMedium) {
                    table.setStorageMedium(dataProperty.getStorageMedium());
                }
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            Preconditions.checkNotNull(dataProperty);
            partitionInfo.setDataProperty(partitionId, dataProperty);
            partitionInfo.setReplicationNum(partitionId, replicationNum);
            partitionInfo.setIsInMemory(partitionId, isInMemory);
            partitionInfo.setTabletType(partitionId, tabletType);
            StorageInfo storageInfo = table.getTableProperty().getStorageInfo();
            StorageCacheInfo storageCacheInfo = storageInfo == null ? null : storageInfo.getStorageCacheInfo();
            partitionInfo.setStorageCacheInfo(partitionId, storageCacheInfo);
        }

        
        String colocateGroup = null;
        try {
            colocateGroup = PropertyAnalyzer.analyzeColocate(properties);
            processColocationProperties(colocateTableIndex, colocateGroup, db, table, false /* expectLakeTable */);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        TStorageType baseIndexStorageType = null;
        try {
            baseIndexStorageType = PropertyAnalyzer.analyzeStorageType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        Preconditions.checkNotNull(baseIndexStorageType);
        
        int schemaVersion = 0;
        try {
            schemaVersion = PropertyAnalyzer.analyzeSchemaVersion(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        int schemaHash = Util.schemaHash(schemaVersion, baseSchema, bfColumns, bfFpp);

        if (stmt.getSortKeys() != null) {
            table.setIndexMeta(baseIndexId, tableName, baseSchema, schemaVersion, schemaHash,
                    shortKeyColumnCount, baseIndexStorageType, keysType, null, sortKeyIdxes);
        } else {
            table.setIndexMeta(baseIndexId, tableName, baseSchema, schemaVersion, schemaHash,
                    shortKeyColumnCount, baseIndexStorageType, keysType, null);
        }

        for (AlterClause alterClause : stmt.getRollupAlterClauseList()) {
            AddRollupClause addRollupClause = (AddRollupClause) alterClause;

            Long baseRollupIndex = table.getIndexIdByName(tableName);

            
            TStorageType rollupIndexStorageType = null;
            try {
                rollupIndexStorageType = PropertyAnalyzer.analyzeStorageType(addRollupClause.getProperties());
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            Preconditions.checkNotNull(rollupIndexStorageType);
            
            List<Column> rollupColumns = stateMgr.getRollupHandler().checkAndPrepareMaterializedView(addRollupClause,
                    table, baseRollupIndex, false);
            short rollupShortKeyColumnCount =
                    GlobalStateMgr.calcShortKeyColumnCount(rollupColumns, alterClause.getProperties());
            int rollupSchemaHash = Util.schemaHash(schemaVersion, rollupColumns, bfColumns, bfFpp);
            long rollupIndexId = metastore.getNextId();
            table.setIndexMeta(rollupIndexId, addRollupClause.getRollupName(), rollupColumns, schemaVersion,
                    rollupSchemaHash, rollupShortKeyColumnCount, rollupIndexStorageType, keysType);
        }

        
        Long version = null;
        try {
            version = PropertyAnalyzer.analyzeVersionInfo(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        Preconditions.checkNotNull(version);

        
        TStorageFormat storageFormat = TStorageFormat.DEFAULT; 
        try {
            storageFormat = PropertyAnalyzer.analyzeStorageFormat(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        table.setStorageFormat(storageFormat);

        
        String storageVolume = RunMode.allowCreateLakeTable() ? "default" : "local";
        table.setStorageVolume(storageVolume);

        
        TCompressionType compressionType = TCompressionType.LZ4_FRAME;
        try {
            compressionType = PropertyAnalyzer.analyzeCompressionType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        table.setCompressionType(compressionType);

        
        int partitionLiveNumber;
        if (properties != null && properties.containsKey(PropertyAnalyzer.PROPERTIES_PARTITION_LIVE_NUMBER)) {
            try {
                partitionLiveNumber = PropertyAnalyzer.analyzePartitionLiveNumber(properties, true);
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            table.setPartitionLiveNumber(partitionLiveNumber);
        }

        try {
            processConstraint(db, table, properties);
        } catch (AnalysisException e) {
            throw new DdlException(
                    String.format("processing constraint failed when creating table:%s. exception msg:%s",
                            table.getName(), e.getMessage()), e);
        }

        
        
        Set<Long> tabletIdSet = new HashSet<Long>();

        boolean createTblSuccess = false;
        boolean addToColocateGroupSuccess = false;
        
        try {
            
            if (table.isOlapOrLakeTable()) {
                if (partitionInfo.getType() == PartitionType.UNPARTITIONED) {
                    if (properties != null && !properties.isEmpty()) {
                        
                        throw new DdlException("Unknown properties: " + properties);
                    }

                    
                    long partitionId = partitionNameToId.get(tableName);
                    Partition partition = metastore.createPartition(db, table, partitionId, tableName, version, tabletIdSet);
                    metastore.buildPartitions(db, table, Collections.singletonList(partition));
                    table.addPartition(partition);
                } else if (partitionInfo.isRangePartition() || partitionInfo.getType() == PartitionType.LIST) {
                    try {
                        
                        
                        boolean hasMedium = false;
                        if (properties != null) {
                            hasMedium = properties.containsKey(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM);
                        }
                        DataProperty dataProperty = PropertyAnalyzer.analyzeDataProperty(properties,
                                DataProperty.getInferredDefaultDataProperty());
                        DynamicPartitionUtil.checkAndSetDynamicPartitionProperty(table, properties);
                        if (table.dynamicPartitionExists() && table.getColocateGroup() != null) {
                            HashDistributionInfo info = (HashDistributionInfo) distributionInfo;
                            if (info.getBucketNum() !=
                                    table.getTableProperty().getDynamicPartitionProperty().getBuckets()) {
                                throw new DdlException("dynamic_partition.buckets should equal the distribution buckets"
                                        + " if creating a colocate table");
                            }
                        }
                        if (hasMedium) {
                            table.setStorageMedium(dataProperty.getStorageMedium());
                        }
                        if (properties != null && !properties.isEmpty()) {
                            
                            throw new DdlException("Unknown properties: " + properties);
                        }
                    } catch (AnalysisException e) {
                        throw new DdlException(e.getMessage());
                    }

                    
                    List<Partition> partitions = new ArrayList<>(partitionNameToId.size());
                    for (Map.Entry<String, Long> entry : partitionNameToId.entrySet()) {
                        Partition partition = metastore.createPartition(db, table, entry.getValue(), entry.getKey(), version,
                                tabletIdSet);
                        partitions.add(partition);
                    }
                    
                    metastore.buildPartitions(db, table, partitions);
                    for (Partition partition : partitions) {
                        table.addPartition(partition);
                    }
                } else {
                    throw new DdlException("Unsupported partition method: " + partitionInfo.getType().name());
                }
                
                
                if (table.isBinlogEnabled()) {
                    Map<String, String> binlogAvailableVersion = table.buildBinlogAvailableVersion();
                    table.setBinlogAvailableVersion(binlogAvailableVersion);
                    LOG.info("set binlog available version when create table, tableName : {}, partitions : {}",
                            tableName, binlogAvailableVersion.toString());
                }
            }

            
            processColocationProperties(colocateTableIndex, colocateGroup, db, table, true /* expectLakeTable */);

            
            if (!metastore.tryLock(false)) {
                throw new DdlException("Failed to acquire globalStateMgr lock. Try again");
            }
            try {
                if (metastore.getDb(db.getId()) == null) {
                    throw new DdlException("database has been dropped when creating table");
                }
                createTblSuccess = db.createTableWithLock(table, false);
                if (!createTblSuccess) {
                    if (db.isInfoSchemaDb()) {
                        ErrorReport.reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "access denied");
                    } else if (!stmt.isSetIfNotExists()) {
                        ErrorReport
                                .reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "table already exists");
                    } else {
                        LOG.info("Create table[{}] which already exists", tableName);
                        return table;
                    }
                }
            } finally {
                metastore.unlock();
            }

            

            
            if (colocateTableIndex.isColocateTable(tableId)) {
                ColocateTableIndex.GroupId groupId = colocateTableIndex.getGroup(tableId);
                List<List<Long>> backendsPerBucketSeq = colocateTableIndex.getBackendsPerBucketSeq(groupId);
                ColocatePersistInfo info =
                        ColocatePersistInfo.createForAddTable(groupId, tableId, backendsPerBucketSeq);
                editLog.logColocateAddTable(info);
                addToColocateGroupSuccess = true;
            }
            LOG.info("Successfully create table[{};{}]", tableName, tableId);
            
            DynamicPartitionUtil.registerOrRemoveDynamicPartitionTable(db.getId(), table);
            DynamicPartitionUtil.registerOrRemovePartitionTTLTable(db.getId(), table);
            stateMgr.getDynamicPartitionScheduler().createOrUpdateRuntimeInfo(
                    tableName, DynamicPartitionScheduler.LAST_UPDATE_TIME, TimeUtils.getCurrentFormatTime());
        } finally {
            if (!createTblSuccess) {
                for (Long tabletId : tabletIdSet) {
                    stateMgr.getCurrentInvertedIndex().deleteTablet(tabletId);
                }
            }
            
            if (colocateTableIndex.isColocateTable(tableId) && !addToColocateGroupSuccess) {
                colocateTableIndex.removeTable(tableId, table, false /* isReplay */);
            }
        }
        if (Config.dynamic_partition_enable && table.getTableProperty().getDynamicPartitionProperty().getEnable()) {
            new Thread(() -> {
                try {
                    GlobalStateMgr.getCurrentState().getDynamicPartitionScheduler()
                            .executeDynamicPartitionForTable(db.getId(), tableId);
                } catch (Exception ex) {
                    LOG.warn("Some problems were encountered in the process of triggering " +
                            "the execution of dynamic partitioning", ex);
                }
            }, "BackgroundDynamicPartitionThread").start();
        }

        return table;
    }

Line commented on: ErrorReport.reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "access denied");

Revised code:
public Table createTable(LocalMetastore metastore, Database db, CreateTableStmt stmt) throws DdlException {
        GlobalStateMgr stateMgr = metastore.getStateMgr();
        EditLog editLog = metastore.getEditLog();
        ColocateTableIndex colocateTableIndex = metastore.getColocateTableIndex();
        String tableName = stmt.getTableName();

        LOG.debug("begin create olap table: {}", tableName);

        
        List<Column> baseSchema = stmt.getColumns();
        metastore.validateColumns(baseSchema);

        
        PartitionDesc partitionDesc = stmt.getPartitionDesc();
        PartitionInfo partitionInfo;
        Map<String, Long> partitionNameToId = Maps.newHashMap();
        if (partitionDesc != null) {
            
            if (partitionDesc instanceof RangePartitionDesc) {
                RangePartitionDesc rangePartitionDesc = (RangePartitionDesc) partitionDesc;
                for (SingleRangePartitionDesc desc : rangePartitionDesc.getSingleRangePartitionDescs()) {
                    long partitionId = metastore.getNextId();
                    partitionNameToId.put(desc.getPartitionName(), partitionId);
                }
            } else if (partitionDesc instanceof ListPartitionDesc) {
                ListPartitionDesc listPartitionDesc = (ListPartitionDesc) partitionDesc;
                listPartitionDesc.findAllPartitionNames()
                        .forEach(partitionName -> partitionNameToId.put(partitionName, metastore.getNextId()));
            } else if (partitionDesc instanceof ExpressionPartitionDesc) {
                ExpressionPartitionDesc expressionPartitionDesc = (ExpressionPartitionDesc) partitionDesc;
                for (SingleRangePartitionDesc desc : expressionPartitionDesc.getRangePartitionDesc()
                        .getSingleRangePartitionDescs()) {
                    long partitionId = metastore.getNextId();
                    partitionNameToId.put(desc.getPartitionName(), partitionId);
                }

                DynamicPartitionUtil.checkIfAutomaticPartitionAllowed(stmt.getProperties());

            } else {
                throw new DdlException("Currently only support range or list partition with engine type olap");
            }
            partitionInfo = partitionDesc.toPartitionInfo(baseSchema, partitionNameToId, false, true);

            
            if (partitionInfo instanceof ExpressionRangePartitionInfo) {
                ExpressionRangePartitionInfo expressionRangePartitionInfo = (ExpressionRangePartitionInfo) partitionInfo;
                long partitionId = metastore.getNextId();
                String replicateNum = stmt.getProperties().get("replication_num");
                expressionRangePartitionInfo.createAutomaticShadowPartition(partitionId, replicateNum);
                partitionNameToId.put(ExpressionRangePartitionInfo.AUTOMATIC_SHADOW_PARTITION_NAME, partitionId);
            }

        } else {
            if (DynamicPartitionUtil.checkDynamicPartitionPropertiesExist(stmt.getProperties())) {
                throw new DdlException("Only support dynamic partition properties on range partition table");
            }
            long partitionId = metastore.getNextId();
            
            partitionNameToId.put(tableName, partitionId);
            partitionInfo = new SinglePartitionInfo();
        }

        
        KeysDesc keysDesc = stmt.getKeysDesc();
        Preconditions.checkNotNull(keysDesc);
        KeysType keysType = keysDesc.getKeysType();

        
        DistributionDesc distributionDesc = stmt.getDistributionDesc();
        Preconditions.checkNotNull(distributionDesc);
        DistributionInfo distributionInfo = distributionDesc.toDistributionInfo(baseSchema);

        short shortKeyColumnCount = 0;
        List<Integer> sortKeyIdxes = new ArrayList<>();
        if (stmt.getSortKeys() != null) {
            List<String> baseSchemaNames = baseSchema.stream().map(Column::getName).collect(Collectors.toList());
            for (String column : stmt.getSortKeys()) {
                int idx = baseSchemaNames.indexOf(column);
                if (idx == -1) {
                    throw new DdlException("Invalid column '" + column + "': not exists in all columns.");
                }
                sortKeyIdxes.add(idx);
            }
            shortKeyColumnCount =
                    GlobalStateMgr.calcShortKeyColumnCount(baseSchema, stmt.getProperties(), sortKeyIdxes);
        } else {
            shortKeyColumnCount = GlobalStateMgr.calcShortKeyColumnCount(baseSchema, stmt.getProperties());
        }
        LOG.debug("create table[{}] short key column count: {}", tableName, shortKeyColumnCount);
        
        TableIndexes indexes = new TableIndexes(stmt.getIndexes());

        
        
        Map<String, String> properties = stmt.getProperties();

        
        long tableId = GlobalStateMgr.getCurrentState().getNextId();
        OlapTable table;
        if (stmt.isExternal()) {
            table = new ExternalOlapTable(db.getId(), tableId, tableName, baseSchema, keysType, partitionInfo,
                    distributionInfo, indexes, properties);
        } else if (stmt.isOlapEngine()) {
            if (distributionInfo.getBucketNum() == 0) {
                int bucketNum = CatalogUtils.calBucketNumAccordingToBackends();
                distributionInfo.setBucketNum(bucketNum);
            }

            RunMode runMode = RunMode.getCurrentRunMode();
            String volume = (properties != null) ? properties.remove(PropertyAnalyzer.PROPERTIES_STORAGE_VOLUME) : null;

            if ("local".equalsIgnoreCase(volume)) {
                table = new OlapTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
            } else if ("default".equalsIgnoreCase(volume)) {
                table = new LakeTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
                metastore.setLakeStorageInfo(table, properties);
            } else if (!Strings.isNullOrEmpty(volume)) {
                throw new DdlException("Unknown storage volume \"" + volume + "\"");
            } else if (runMode == RunMode.SHARED_DATA) {
                table = new LakeTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
                metastore.setLakeStorageInfo(table, properties);
            } else {
                table = new OlapTable(tableId, tableName, baseSchema, keysType, partitionInfo, distributionInfo, indexes);
            }

            if (table.isLakeTable() && !runMode.isAllowCreateLakeTable())  {
                throw new DdlException("Cannot create table with persistent volume in current run mode \"" + runMode + "\"");
            }
            if (table.isOlapTable() && !runMode.isAllowCreateOlapTable()) {
                throw new DdlException("Cannot create table without persistent volume in current run mode \"" + runMode + "\"");
            }
        } else {
            throw new DdlException("Unrecognized engine \"" + stmt.getEngineName() + "\"");
        }

        table.setComment(stmt.getComment());

        
        long baseIndexId = metastore.getNextId();
        table.setBaseIndexId(baseIndexId);

        
        Set<String> bfColumns = null;
        double bfFpp = 0;
        try {
            bfColumns = PropertyAnalyzer.analyzeBloomFilterColumns(properties, baseSchema,
                    table.getKeysType() == KeysType.PRIMARY_KEYS);
            if (bfColumns != null && bfColumns.isEmpty()) {
                bfColumns = null;
            }

            bfFpp = PropertyAnalyzer.analyzeBloomFilterFpp(properties);
            if (bfColumns != null && bfFpp == 0) {
                bfFpp = FeConstants.DEFAULT_BLOOM_FILTER_FPP;
            } else if (bfColumns == null) {
                bfFpp = 0;
            }

            table.setBloomFilterInfo(bfColumns, bfFpp);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        short replicationNum = RunMode.defaultReplicationNum();
        try {
            boolean isReplicationNumSet =
                    properties != null && properties.containsKey(PropertyAnalyzer.PROPERTIES_REPLICATION_NUM);
            replicationNum = PropertyAnalyzer.analyzeReplicationNum(properties, replicationNum);
            if (isReplicationNumSet) {
                table.setReplicationNum(replicationNum);
            }
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        boolean isInMemory =
                PropertyAnalyzer.analyzeBooleanProp(properties, PropertyAnalyzer.PROPERTIES_INMEMORY, false);
        table.setIsInMemory(isInMemory);

        boolean enablePersistentIndex =
                PropertyAnalyzer.analyzeBooleanProp(properties, PropertyAnalyzer.PROPERTIES_ENABLE_PERSISTENT_INDEX,
                        false);
        table.setEnablePersistentIndex(enablePersistentIndex);

        if (properties != null && (properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_ENABLE) ||
                properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_MAX_SIZE) ||
                properties.containsKey(PropertyAnalyzer.PROPERTIES_BINLOG_TTL))) {
            try {
                boolean enableBinlog = PropertyAnalyzer.analyzeBooleanProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_ENABLE, false);
                long binlogTtl = PropertyAnalyzer.analyzeLongProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_TTL, Config.binlog_ttl_second);
                long binlogMaxSize = PropertyAnalyzer.analyzeLongProp(properties,
                        PropertyAnalyzer.PROPERTIES_BINLOG_MAX_SIZE, Config.binlog_max_size);
                BinlogConfig binlogConfig = new BinlogConfig(0, enableBinlog,
                        binlogTtl, binlogMaxSize);
                table.setCurBinlogConfig(binlogConfig);
                LOG.info("create table {} set binlog config, enable_binlog = {}, binlogTtl = {}, binlog_max_size = {}",
                        tableName, enableBinlog, binlogTtl, binlogMaxSize);
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
        }

        
        try {
            table.setWriteQuorum(PropertyAnalyzer.analyzeWriteQuorum(properties));
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        if (table.hasAutoIncrementColumn() && stmt.isOlapEngine()
                && RunMode.allowCreateLakeTable()) {
            throw new DdlException("Table with AUTO_INCREMENT column can not be lake table");
        }

        
        table.setEnableReplicatedStorage(
                PropertyAnalyzer.analyzeBooleanProp(
                        properties, PropertyAnalyzer.PROPERTIES_REPLICATED_STORAGE,
                        Config.enable_replicated_storage_as_default_engine));

        if (table.enableReplicatedStorage().equals(false)) {
            for (Column col : baseSchema) {
                if (col.isAutoIncrement()) {
                    throw new DdlException("Table with AUTO_INCREMENT column must use Replicated Storage");
                }
            }
        }

        TTabletType tabletType = TTabletType.TABLET_TYPE_DISK;
        try {
            tabletType = PropertyAnalyzer.analyzeTabletType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        if (partitionInfo.getType() == PartitionType.UNPARTITIONED) {
            
            

            
            long partitionId = partitionNameToId.get(tableName);
            DataProperty dataProperty = null;
            try {
                boolean hasMedium = false;
                if (properties != null) {
                    hasMedium = properties.containsKey(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM);
                }
                dataProperty = PropertyAnalyzer.analyzeDataProperty(properties,
                        DataProperty.getInferredDefaultDataProperty());
                if (hasMedium) {
                    table.setStorageMedium(dataProperty.getStorageMedium());
                }
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            Preconditions.checkNotNull(dataProperty);
            partitionInfo.setDataProperty(partitionId, dataProperty);
            partitionInfo.setReplicationNum(partitionId, replicationNum);
            partitionInfo.setIsInMemory(partitionId, isInMemory);
            partitionInfo.setTabletType(partitionId, tabletType);
            StorageInfo storageInfo = table.getTableProperty().getStorageInfo();
            StorageCacheInfo storageCacheInfo = storageInfo == null ? null : storageInfo.getStorageCacheInfo();
            partitionInfo.setStorageCacheInfo(partitionId, storageCacheInfo);
        }

        
        String colocateGroup = null;
        try {
            colocateGroup = PropertyAnalyzer.analyzeColocate(properties);
            processColocationProperties(colocateTableIndex, colocateGroup, db, table, false /* expectLakeTable */);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }

        
        TStorageType baseIndexStorageType = null;
        try {
            baseIndexStorageType = PropertyAnalyzer.analyzeStorageType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        Preconditions.checkNotNull(baseIndexStorageType);
        
        int schemaVersion = 0;
        try {
            schemaVersion = PropertyAnalyzer.analyzeSchemaVersion(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        int schemaHash = Util.schemaHash(schemaVersion, baseSchema, bfColumns, bfFpp);

        if (stmt.getSortKeys() != null) {
            table.setIndexMeta(baseIndexId, tableName, baseSchema, schemaVersion, schemaHash,
                    shortKeyColumnCount, baseIndexStorageType, keysType, null, sortKeyIdxes);
        } else {
            table.setIndexMeta(baseIndexId, tableName, baseSchema, schemaVersion, schemaHash,
                    shortKeyColumnCount, baseIndexStorageType, keysType, null);
        }

        for (AlterClause alterClause : stmt.getRollupAlterClauseList()) {
            AddRollupClause addRollupClause = (AddRollupClause) alterClause;

            Long baseRollupIndex = table.getIndexIdByName(tableName);

            
            TStorageType rollupIndexStorageType = null;
            try {
                rollupIndexStorageType = PropertyAnalyzer.analyzeStorageType(addRollupClause.getProperties());
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            Preconditions.checkNotNull(rollupIndexStorageType);
            
            List<Column> rollupColumns = stateMgr.getRollupHandler().checkAndPrepareMaterializedView(addRollupClause,
                    table, baseRollupIndex, false);
            short rollupShortKeyColumnCount =
                    GlobalStateMgr.calcShortKeyColumnCount(rollupColumns, alterClause.getProperties());
            int rollupSchemaHash = Util.schemaHash(schemaVersion, rollupColumns, bfColumns, bfFpp);
            long rollupIndexId = metastore.getNextId();
            table.setIndexMeta(rollupIndexId, addRollupClause.getRollupName(), rollupColumns, schemaVersion,
                    rollupSchemaHash, rollupShortKeyColumnCount, rollupIndexStorageType, keysType);
        }

        
        Long version = null;
        try {
            version = PropertyAnalyzer.analyzeVersionInfo(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        Preconditions.checkNotNull(version);

        
        TStorageFormat storageFormat = TStorageFormat.DEFAULT; 
        try {
            storageFormat = PropertyAnalyzer.analyzeStorageFormat(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        table.setStorageFormat(storageFormat);

        
        String storageVolume = RunMode.allowCreateLakeTable() ? "default" : "local";
        table.setStorageVolume(storageVolume);

        
        TCompressionType compressionType = TCompressionType.LZ4_FRAME;
        try {
            compressionType = PropertyAnalyzer.analyzeCompressionType(properties);
        } catch (AnalysisException e) {
            throw new DdlException(e.getMessage());
        }
        table.setCompressionType(compressionType);

        
        int partitionLiveNumber;
        if (properties != null && properties.containsKey(PropertyAnalyzer.PROPERTIES_PARTITION_LIVE_NUMBER)) {
            try {
                partitionLiveNumber = PropertyAnalyzer.analyzePartitionLiveNumber(properties, true);
            } catch (AnalysisException e) {
                throw new DdlException(e.getMessage());
            }
            table.setPartitionLiveNumber(partitionLiveNumber);
        }

        try {
            processConstraint(db, table, properties);
        } catch (AnalysisException e) {
            throw new DdlException(
                    String.format("processing constraint failed when creating table:%s. exception msg:%s",
                            table.getName(), e.getMessage()), e);
        }

        
        
        Set<Long> tabletIdSet = new HashSet<Long>();

        boolean createTblSuccess = false;
        boolean addToColocateGroupSuccess = false;
        
        try {
            
            if (table.isOlapOrLakeTable()) {
                if (partitionInfo.getType() == PartitionType.UNPARTITIONED) {
                    if (properties != null && !properties.isEmpty()) {
                        
                        throw new DdlException("Unknown properties: " + properties);
                    }

                    
                    long partitionId = partitionNameToId.get(tableName);
                    Partition partition = metastore.createPartition(db, table, partitionId, tableName, version, tabletIdSet);
                    metastore.buildPartitions(db, table, Collections.singletonList(partition));
                    table.addPartition(partition);
                } else if (partitionInfo.isRangePartition() || partitionInfo.getType() == PartitionType.LIST) {
                    try {
                        
                        
                        boolean hasMedium = false;
                        if (properties != null) {
                            hasMedium = properties.containsKey(PropertyAnalyzer.PROPERTIES_STORAGE_MEDIUM);
                        }
                        DataProperty dataProperty = PropertyAnalyzer.analyzeDataProperty(properties,
                                DataProperty.getInferredDefaultDataProperty());
                        DynamicPartitionUtil.checkAndSetDynamicPartitionProperty(table, properties);
                        if (table.dynamicPartitionExists() && table.getColocateGroup() != null) {
                            HashDistributionInfo info = (HashDistributionInfo) distributionInfo;
                            if (info.getBucketNum() !=
                                    table.getTableProperty().getDynamicPartitionProperty().getBuckets()) {
                                throw new DdlException("dynamic_partition.buckets should equal the distribution buckets"
                                        + " if creating a colocate table");
                            }
                        }
                        if (hasMedium) {
                            table.setStorageMedium(dataProperty.getStorageMedium());
                        }
                        if (properties != null && !properties.isEmpty()) {
                            
                            throw new DdlException("Unknown properties: " + properties);
                        }
                    } catch (AnalysisException e) {
                        throw new DdlException(e.getMessage());
                    }

                    
                    List<Partition> partitions = new ArrayList<>(partitionNameToId.size());
                    for (Map.Entry<String, Long> entry : partitionNameToId.entrySet()) {
                        Partition partition = metastore.createPartition(db, table, entry.getValue(), entry.getKey(), version,
                                tabletIdSet);
                        partitions.add(partition);
                    }
                    
                    metastore.buildPartitions(db, table, partitions);
                    for (Partition partition : partitions) {
                        table.addPartition(partition);
                    }
                } else {
                    throw new DdlException("Unsupported partition method: " + partitionInfo.getType().name());
                }
                
                
                if (table.isBinlogEnabled()) {
                    Map<String, String> binlogAvailableVersion = table.buildBinlogAvailableVersion();
                    table.setBinlogAvailableVersion(binlogAvailableVersion);
                    LOG.info("set binlog available version when create table, tableName : {}, partitions : {}",
                            tableName, binlogAvailableVersion.toString());
                }
            }

            
            processColocationProperties(colocateTableIndex, colocateGroup, db, table, true /* expectLakeTable */);

            
            if (!metastore.tryLock(false)) {
                throw new DdlException("Failed to acquire globalStateMgr lock. Try again");
            }
            try {
                if (metastore.getDb(db.getId()) == null) {
                    throw new DdlException("database has been dropped when creating table");
                }
                createTblSuccess = db.createTableWithLock(table, false);
                if (!createTblSuccess) {
                    if (db.isInfoSchemaDb()) {
                        ErrorReport.reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "create denied");
                    } else if (!stmt.isSetIfNotExists()) {
                        ErrorReport
                                .reportDdlException(ErrorCode.ERR_CANT_CREATE_TABLE, tableName, "table already exists");
                    } else {
                        LOG.info("Create table[{}] which already exists", tableName);
                        return table;
                    }
                }
            } finally {
                metastore.unlock();
            }

            

            
            if (colocateTableIndex.isColocateTable(tableId)) {
                ColocateTableIndex.GroupId groupId = colocateTableIndex.getGroup(tableId);
                List<List<Long>> backendsPerBucketSeq = colocateTableIndex.getBackendsPerBucketSeq(groupId);
                ColocatePersistInfo info =
                        ColocatePersistInfo.createForAddTable(groupId, tableId, backendsPerBucketSeq);
                editLog.logColocateAddTable(info);
                addToColocateGroupSuccess = true;
            }
            LOG.info("Successfully create table[{};{}]", tableName, tableId);
            
            DynamicPartitionUtil.registerOrRemoveDynamicPartitionTable(db.getId(), table);
            DynamicPartitionUtil.registerOrRemovePartitionTTLTable(db.getId(), table);
            stateMgr.getDynamicPartitionScheduler().createOrUpdateRuntimeInfo(
                    tableName, DynamicPartitionScheduler.LAST_UPDATE_TIME, TimeUtils.getCurrentFormatTime());
        } finally {
            if (!createTblSuccess) {
                for (Long tabletId : tabletIdSet) {
                    stateMgr.getCurrentInvertedIndex().deleteTablet(tabletId);
                }
            }
            
            if (colocateTableIndex.isColocateTable(tableId) && !addToColocateGroupSuccess) {
                colocateTableIndex.removeTable(tableId, table, false /* isReplay */);
            }
        }
        if (Config.dynamic_partition_enable && table.getTableProperty().getDynamicPartitionProperty().getEnable()) {
            new Thread(() -> {
                try {
                    GlobalStateMgr.getCurrentState().getDynamicPartitionScheduler()
                            .executeDynamicPartitionForTable(db.getId(), tableId);
                } catch (Exception ex) {
                    LOG.warn("Some problems were encountered in the process of triggering " +
                            "the execution of dynamic partitioning", ex);
                }
            }, "BackgroundDynamicPartitionThread").start();
        }

        return table;
    }