This is an automated email from the ASF dual-hosted git repository. morningman pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new 6baa694bc1 [feature-wip](multi-catalog) Catalog operation syntax (#10033) 6baa694bc1 is described below commit 6baa694bc1b7681fb227dc926db732ff127c64a4 Author: huangzhaowei <huangzhaowei....@bytedance.com> AuthorDate: Fri Jun 17 17:50:31 2022 +0800 [feature-wip](multi-catalog) Catalog operation syntax (#10033) Impl catalog operation syntax --- fe/fe-core/src/main/cup/sql_parser.cup | 36 ++- .../doris/analysis/AlterCatalogNameStmt.java | 91 ++++++++ .../doris/analysis/AlterCatalogPropertyStmt.java | 73 ++++++ .../apache/doris/analysis/CreateCatalogStmt.java | 99 +++++++++ .../org/apache/doris/analysis/DropCatalogStmt.java | 82 +++++++ .../org/apache/doris/analysis/ShowCatalogStmt.java | 94 ++++++++ .../java/org/apache/doris/catalog/Catalog.java | 27 +++ .../java/org/apache/doris/common/ErrorCode.java | 2 +- .../java/org/apache/doris/common/FeNameFormat.java | 11 + .../apache/doris/datasource/CatalogFactory.java | 82 +++++++ .../{DataSourceProperty.java => CatalogLog.java} | 29 ++- .../org/apache/doris/datasource/DataSourceIf.java | 9 +- .../org/apache/doris/datasource/DataSourceMgr.java | 246 ++++++++++++++------- .../doris/datasource/DataSourceProperty.java | 5 + .../doris/datasource/EsExternalDataSource.java | 10 + .../doris/datasource/ExternalDataSource.java | 20 +- .../doris/datasource/HMSExternalDataSource.java | 36 +-- .../doris/datasource/InternalDataSource.java | 15 ++ .../org/apache/doris/journal/JournalEntity.java | 9 + .../java/org/apache/doris/persist/EditLog.java | 25 +++ .../org/apache/doris/persist/OperationType.java | 6 + .../doris/persist/meta/MetaPersistMethod.java | 6 + .../doris/persist/meta/PersistMetaModules.java | 2 +- .../main/java/org/apache/doris/qe/DdlExecutor.java | 12 + .../java/org/apache/doris/qe/ShowExecutor.java | 7 + fe/fe-core/src/main/jflex/sql_scanner.flex | 2 + .../doris/analysis/AlterCatalogNameStmtTest.java | 94 ++++++++ .../doris/analysis/AlterCatalogPropsStmtTest.java | 91 ++++++++ .../doris/analysis/CreateCatalogStmtTest.java | 93 ++++++++ .../apache/doris/analysis/DropCatalogStmtTest.java | 72 ++++++ .../apache/doris/analysis/ShowCatalogStmtTest.java | 44 ++++ .../apache/doris/datasource/DatasourceMgrTest.java | 96 ++++++++ 32 files changed, 1425 insertions(+), 101 deletions(-) diff --git a/fe/fe-core/src/main/cup/sql_parser.cup b/fe/fe-core/src/main/cup/sql_parser.cup index c9494434d9..6b67f7dd58 100644 --- a/fe/fe-core/src/main/cup/sql_parser.cup +++ b/fe/fe-core/src/main/cup/sql_parser.cup @@ -277,7 +277,8 @@ terminal String KW_ADD, KW_ADMIN, KW_AFTER, KW_AGGREGATE, KW_ALIAS, KW_ALL, KW_A KW_VALUE, KW_VALUES, KW_VARCHAR, KW_VARIABLES, KW_VERBOSE, KW_VIEW, KW_WARNINGS, KW_WEEK, KW_WHEN, KW_WHITELIST, KW_WHERE, KW_WITH, KW_WORK, KW_WRITE, KW_YEAR, - KW_NOT_NULL; + KW_NOT_NULL, + KW_CATALOG, KW_CATALOGS; terminal COMMA, COLON, DOT, DOTDOTDOT, AT, STAR, LPAREN, RPAREN, SEMICOLON, LBRACKET, RBRACKET, DIVIDE, MOD, ADD, SUBTRACT; terminal BITAND, BITOR, BITXOR, BITNOT; @@ -863,6 +864,15 @@ alter_stmt ::= {: RESULT = new AlterDatabasePropertyStmt(dbName, map); :} + /* Catalog */ + | KW_ALTER KW_CATALOG ident:catalogName KW_RENAME ident:newCatalogName + {: + RESULT = new AlterCatalogNameStmt(catalogName, newCatalogName); + :} + | KW_ALTER KW_CATALOG ident:catalogName KW_SET KW_PROPERTIES LPAREN key_value_map:map RPAREN + {: + RESULT = new AlterCatalogPropertyStmt(catalogName, map); + :} | KW_ALTER KW_RESOURCE ident_or_text:resourceName opt_properties:properties {: RESULT = new AlterResourceStmt(resourceName, properties); @@ -1225,6 +1235,11 @@ create_stmt ::= {: RESULT = new CreateDbStmt(ifNotExists, db, null); :} + /* Catalog */ + | KW_CREATE KW_CATALOG opt_if_not_exists:ifNotExists ident:catalogName opt_properties:properties + {: + RESULT = new CreateCatalogStmt(ifNotExists, catalogName, properties); + :} /* cluster */ /* KW_CREATE KW_CLUSTER ident:name opt_properties:properties KW_IDENTIFIED KW_BY STRING_LITERAL:password {: @@ -2014,6 +2029,11 @@ drop_stmt ::= {: RESULT = new DropDbStmt(ifExists, db, force); :} + /* Catalog */ + | KW_DROP KW_CATALOG opt_if_exists:ifExists ident:catalogName + {: + RESULT = new DropCatalogStmt(ifExists, catalogName); + :} /* cluster */ | KW_DROP KW_CLUSTER opt_if_exists:ifExists ident:cluster {: @@ -2705,6 +2725,16 @@ show_param ::= {: RESULT = new ShowDbStmt(parser.wild, parser.where); :} + /* Catalog */ + | KW_CATALOGS + {: + RESULT = new ShowCatalogStmt(); + :} + /* show Catalog name */ + | KW_CATALOG ident:catalogName + {: + RESULT = new ShowCatalogStmt(catalogName); + :} /* Dynamic Partition */ | KW_DYNAMIC KW_PARTITION KW_TABLES opt_db:db {: @@ -5826,6 +5856,10 @@ keyword ::= {: RESULT = id; :} | KW_CURRENT_TIMESTAMP:id {: RESULT = id; :} + | KW_CATALOG:id + {: RESULT = id; :} + | KW_CATALOGS:id + {: RESULT = id; :} ; // Identifier that contain keyword diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogNameStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogNameStmt.java new file mode 100644 index 0000000000..e8eb8bb473 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogNameStmt.java @@ -0,0 +1,91 @@ +// 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.doris.analysis; + + +import org.apache.doris.analysis.CompoundPredicate.Operator; +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.FeNameFormat; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.PaloPrivilege; +import org.apache.doris.mysql.privilege.PrivBitSet; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.base.Strings; + +/** + * Statement for alter the catalog name. + */ +public class AlterCatalogNameStmt extends DdlStmt { + private final String catalogName; + private final String newCatalogName; + + public AlterCatalogNameStmt(String catalogName, String newCatalogName) { + this.catalogName = catalogName; + this.newCatalogName = newCatalogName; + } + + public String getCatalogName() { + return catalogName; + } + + public String getNewCatalogName() { + return newCatalogName; + } + + @Override + public void analyze(Analyzer analyzer) throws UserException { + super.analyze(analyzer); + if (!Config.enable_multi_catalog) { + throw new AnalysisException("The multi-catalog feature is still in experiment, and you can enable it " + + "manually by set fe configuration named `enable_multi_catalog` to be ture."); + } + if (Strings.isNullOrEmpty(catalogName)) { + throw new AnalysisException("Datasource name is not set"); + } + + if (catalogName.equals(InternalDataSource.INTERNAL_DS_NAME)) { + throw new AnalysisException("Internal catalog can't be alter."); + } + + if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), + PrivPredicate.of(PrivBitSet.of(PaloPrivilege.ADMIN_PRIV, PaloPrivilege.ALTER_PRIV), Operator.OR))) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, + analyzer.getQualifiedUser(), catalogName); + } + + if (Strings.isNullOrEmpty(newCatalogName)) { + throw new AnalysisException("New catalog name is not set"); + } + if (newCatalogName.equals(InternalDataSource.INTERNAL_DS_NAME)) { + throw new AnalysisException("Cannot alter a catalog into a build-in name."); + } + FeNameFormat.checkCommonName("catalog", newCatalogName); + } + + @Override + public String toSql() { + return "ALTER CATALOG " + catalogName + " RENAME " + newCatalogName; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogPropertyStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogPropertyStmt.java new file mode 100644 index 0000000000..7f39780eca --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AlterCatalogPropertyStmt.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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.FeNameFormat; +import org.apache.doris.common.UserException; +import org.apache.doris.common.util.PrintableMap; +import org.apache.doris.datasource.InternalDataSource; + +import com.google.common.base.Strings; + +import java.util.Map; + +/** + * Statement for alter the catalog property. + */ +public class AlterCatalogPropertyStmt extends DdlStmt { + private final String catalogName; + private final Map<String, String> newProperties; + + public AlterCatalogPropertyStmt(String catalogName, Map<String, String> newProperties) { + this.catalogName = catalogName; + this.newProperties = newProperties; + } + + public String getCatalogName() { + return catalogName; + } + + public Map<String, String> getNewProperties() { + return newProperties; + } + + @Override + public void analyze(Analyzer analyzer) throws UserException { + super.analyze(analyzer); + if (!Config.enable_multi_catalog) { + throw new AnalysisException("The multi-catalog feature is still in experiment, and you can enable it " + + "manually by set fe configuration named `enable_multi_catalog` to be ture."); + } + if (Strings.isNullOrEmpty(catalogName)) { + throw new AnalysisException("Datasource name is not set"); + } + + if (catalogName.equals(InternalDataSource.INTERNAL_DS_NAME)) { + throw new AnalysisException("Internal catalog can't be alter."); + } + FeNameFormat.checkCatalogProperties(newProperties); + } + + @Override + public String toSql() { + return "ALTER CATALOG " + catalogName + " SET PROPERTIES (" + + new PrintableMap<>(newProperties, "=", true, false, ",") + ")"; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateCatalogStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateCatalogStmt.java new file mode 100644 index 0000000000..ee89675d6c --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/CreateCatalogStmt.java @@ -0,0 +1,99 @@ +// 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.doris.analysis; + +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.FeNameFormat; +import org.apache.doris.common.UserException; +import org.apache.doris.common.util.PrintableMap; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; + +import java.util.HashMap; +import java.util.Map; + +/** + * Statement for create a new catalog. + */ +public class CreateCatalogStmt extends DdlStmt { + private final boolean ifNotExists; + private final String catalogName; + private final Map<String, String> properties; + + /** + * Statement for create a new catalog. + */ + public CreateCatalogStmt(boolean ifNotExists, String catalogName, Map<String, String> properties) { + this.ifNotExists = ifNotExists; + this.catalogName = catalogName; + this.properties = properties == null ? new HashMap<>() : properties; + } + + public String getCatalogName() { + return catalogName; + } + + public Map<String, String> getProperties() { + return properties; + } + + public boolean isSetIfNotExists() { + return ifNotExists; + } + + @Override + public void analyze(Analyzer analyzer) throws UserException { + super.analyze(analyzer); + if (!Config.enable_multi_catalog) { + throw new AnalysisException("The multi-catalog feature is still in experiment, and you can enable it " + + "manually by set fe configuration named `enable_multi_catalog` to be ture."); + } + if (catalogName.equals(InternalDataSource.INTERNAL_DS_NAME)) { + throw new AnalysisException("Internal catalog name can't be create."); + } + FeNameFormat.checkCommonName("catalog", catalogName); + + if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.CREATE)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, + analyzer.getQualifiedUser(), catalogName); + } + FeNameFormat.checkCatalogProperties(properties); + } + + @Override + public String toString() { + return toSql(); + } + + @Override + public String toSql() { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("CREATE CATALOG ").append("`").append(catalogName).append("`"); + if (properties.size() > 0) { + stringBuilder.append("\nPROPERTIES (\n"); + stringBuilder.append(new PrintableMap<>(properties, "=", true, true, false)); + stringBuilder.append("\n)"); + } + return stringBuilder.toString(); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/DropCatalogStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropCatalogStmt.java new file mode 100644 index 0000000000..aa620b17a4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/DropCatalogStmt.java @@ -0,0 +1,82 @@ +// 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.doris.analysis; + +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.base.Strings; + +/** + * Statement for drop a catalog. + */ +public class DropCatalogStmt extends DdlStmt { + private final boolean ifExists; + private final String catalogName; + + public DropCatalogStmt(boolean ifExists, String catalogName) { + this.ifExists = ifExists; + this.catalogName = catalogName; + } + + public boolean isSetIfExists() { + return ifExists; + } + + public String getCatalogName() { + return this.catalogName; + } + + @Override + public void analyze(Analyzer analyzer) throws UserException { + super.analyze(analyzer); + if (!Config.enable_multi_catalog) { + throw new AnalysisException("The multi-catalog feature is still in experiment, and you can enable it " + + "manually by set fe configuration named `enable_multi_catalog` to be ture."); + } + if (Strings.isNullOrEmpty(catalogName)) { + throw new AnalysisException("Datasource name is not set"); + } + + if (catalogName.equals(InternalDataSource.INTERNAL_DS_NAME)) { + throw new AnalysisException("Internal catalog can't be drop."); + } + + if (!Catalog.getCurrentCatalog().getAuth().checkGlobalPriv(ConnectContext.get(), PrivPredicate.DROP)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DBACCESS_DENIED_ERROR, + ConnectContext.get().getQualifiedUser(), catalogName); + } + } + + @Override + public String toSql() { + return "DROP CATALOG " + "`" + catalogName + "`"; + } + + @Override + public String toString() { + return toSql(); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCatalogStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCatalogStmt.java new file mode 100644 index 0000000000..b075f07661 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCatalogStmt.java @@ -0,0 +1,94 @@ +// 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.doris.analysis; + +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.UserException; +import org.apache.doris.qe.ShowResultSetMetaData; + +/** + * Statement for show all catalog or desc the specific catalog. + */ +public class ShowCatalogStmt extends ShowStmt { + private static final ShowResultSetMetaData META_DATA_ALL = + ShowResultSetMetaData.builder() + .addColumn(new Column("CatalogName", ScalarType.createVarchar(64))) + .addColumn(new Column("Type", ScalarType.createStringType())) + .build(); + + private static final ShowResultSetMetaData META_DATA_SPECIFIC = + ShowResultSetMetaData.builder() + .addColumn(new Column("Key", ScalarType.createStringType())) + .addColumn(new Column("Value", ScalarType.createStringType())) + .build(); + + private final String catalogName; + + public ShowCatalogStmt(String catalogName) { + this.catalogName = catalogName; + } + + public ShowCatalogStmt() { + this.catalogName = null; + } + + public String getCatalogName() { + return catalogName; + } + + @Override + public void analyze(Analyzer analyzer) throws AnalysisException, UserException { + if (!Config.enable_multi_catalog) { + throw new AnalysisException("The multi-catalog feature is still in experiment, and you can enable it " + + "manually by set fe configuration named `enable_multi_catalog` to be ture."); + } + super.analyze(analyzer); + } + + @Override + public String toSql() { + StringBuilder sb = new StringBuilder(); + sb.append("SHOW"); + + if (catalogName != null) { + sb.append(" CATALOG "); + sb.append(catalogName); + } else { + sb.append(" CATALOGS"); + } + + return sb.toString(); + } + + @Override + public String toString() { + return toSql(); + } + + @Override + public ShowResultSetMetaData getMetaData() { + if (catalogName == null) { + return META_DATA_ALL; + } else { + return META_DATA_SPECIFIC; + } + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java index 61419eb4f4..1d649a29a7 100755 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Catalog.java @@ -1862,6 +1862,22 @@ public class Catalog { return checksum; } + /** + * Load datasource through file. + **/ + public long loadDatasource(DataInputStream in, long checksum) throws IOException { + if (Config.enable_multi_catalog) { + DataSourceMgr mgr = DataSourceMgr.read(in); + // When enable the multi catalog in the first time, the mgr will be a null value. + // So ignore it to use default datasource manager. + if (mgr != null) { + this.dataSourceMgr = mgr; + } + LOG.info("finished replay datasource from image"); + } + return checksum; + } + // Only called by checkpoint thread // return the latest image file's absolute path public String saveImage() throws IOException { @@ -2126,6 +2142,17 @@ public class Catalog { return checksum; } + /** + * Save datasource image. + */ + public long saveDatasource(CountingDataOutputStream out, long checksum) throws IOException { + // Do not write datasource image when enable multi catalog is false. + if (Config.enable_multi_catalog) { + Catalog.getCurrentCatalog().getDataSourceMgr().write(out); + } + return checksum; + } + public void createLabelCleaner() { labelCleaner = new MasterDaemon("LoadLabelCleaner", Config.label_clean_interval_second * 1000L) { @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java index a1ae7c0f5f..35dedadc30 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/ErrorCode.java @@ -1685,7 +1685,7 @@ public enum ErrorCode { "data cannot be inserted into table with empty partition. " + "Use `SHOW PARTITIONS FROM %s` to see the currently partitions of this table. "), ERROR_SQL_AND_LIMITATIONS_SET_IN_ONE_RULE(5084, new byte[]{'4', '2', '0', '0', '0'}, - "sql/sqlHash and partition_num/tablet_num/cardinality cannot be set in one rule.") + "sql/sqlHash and partition_num/tablet_num/cardinality cannot be set in one rule."), ; // This is error code diff --git a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java index 13b4a6fc48..1f3db6a4a6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java +++ b/fe/fe-core/src/main/java/org/apache/doris/common/FeNameFormat.java @@ -23,6 +23,8 @@ import org.apache.doris.system.SystemInfoService; import com.google.common.base.Strings; +import java.util.Map; + public class FeNameFormat { private static final String LABEL_REGEX = "^[-_A-Za-z0-9]{1,128}$"; private static final String COMMON_NAME_REGEX = "^[a-zA-Z][a-zA-Z0-9_]{0,63}$"; @@ -112,4 +114,13 @@ public class FeNameFormat { ErrorReport.reportAnalysisException(ErrorCode.ERR_WRONG_NAME_FORMAT, type, name); } } + + /** + * Check the type property of the catalog props. + */ + public static void checkCatalogProperties(Map<String, String> props) throws AnalysisException { + if (!props.containsKey("type")) { + throw new AnalysisException("All the external catalog should contain the type property."); + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogFactory.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogFactory.java new file mode 100644 index 0000000000..2995c37d1c --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogFactory.java @@ -0,0 +1,82 @@ +// 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.doris.datasource; + +import org.apache.doris.analysis.AlterCatalogNameStmt; +import org.apache.doris.analysis.AlterCatalogPropertyStmt; +import org.apache.doris.analysis.CreateCatalogStmt; +import org.apache.doris.analysis.DropCatalogStmt; +import org.apache.doris.analysis.ShowCatalogStmt; +import org.apache.doris.analysis.StatementBase; + +import java.util.Map; + +/** + * A factory to create catalog instance of log or covert catalog into log. + */ +public class CatalogFactory { + + /** + * Convert the sql statement into catalog log. + */ + public static CatalogLog constructorCatalogLog(StatementBase stmt) { + CatalogLog log = new CatalogLog(); + if (stmt instanceof CreateCatalogStmt) { + log.setCatalogName(((CreateCatalogStmt) stmt).getCatalogName()); + log.setProps(((CreateCatalogStmt) stmt).getProperties()); + } else if (stmt instanceof DropCatalogStmt) { + log.setCatalogName(((DropCatalogStmt) stmt).getCatalogName()); + } else if (stmt instanceof AlterCatalogPropertyStmt) { + log.setCatalogName(((AlterCatalogPropertyStmt) stmt).getCatalogName()); + log.setNewProps(((AlterCatalogPropertyStmt) stmt).getNewProperties()); + } else if (stmt instanceof AlterCatalogNameStmt) { + log.setCatalogName(((AlterCatalogNameStmt) stmt).getCatalogName()); + log.setNewCatalogName(((AlterCatalogNameStmt) stmt).getNewCatalogName()); + } else if (stmt instanceof ShowCatalogStmt) { + if (((ShowCatalogStmt) stmt).getCatalogName() != null) { + log.setCatalogName(((ShowCatalogStmt) stmt).getCatalogName()); + } + } else { + throw new RuntimeException("Unknown stmt for datasource manager " + stmt.getClass().getSimpleName()); + } + return log; + } + + /** + * create the datasource instance from data source log. + */ + public static DataSourceIf constructorFromLog(CatalogLog log) { + return constructorDataSource(log.getCatalogName(), log.getProps()); + } + + private static DataSourceIf constructorDataSource(String name, Map<String, String> props) { + String type = props.get("type"); + DataSourceIf dataSource; + switch (type) { + case "hms": + dataSource = new HMSExternalDataSource(name, props); + break; + case "es": + dataSource = new EsExternalDataSource(name, props); + break; + default: + throw new RuntimeException("Unknown datasource type for " + type); + } + return dataSource; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogLog.java similarity index 65% copy from fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java copy to fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogLog.java index 43bc87fb7d..d887a1d053 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/CatalogLog.java @@ -21,25 +21,42 @@ import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; import org.apache.doris.persist.gson.GsonUtils; -import com.google.common.collect.Maps; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Map; -public class DataSourceProperty implements Writable { - @SerializedName(value = "properties") - private Map<String, String> properties = Maps.newHashMap(); +/** + * A union metadata log for all the catalog operator include create,drop and alter. + */ +@NoArgsConstructor +@Getter +@Data +public class CatalogLog implements Writable { + @SerializedName(value = "catalogName") + private String catalogName; + + @SerializedName(value = "props") + private Map<String, String> props; + + @SerializedName(value = "newCatalogName") + private String newCatalogName; + + @SerializedName(value = "newProps") + private Map<String, String> newProps; @Override public void write(DataOutput out) throws IOException { Text.writeString(out, GsonUtils.GSON.toJson(this)); } - public static DataSourceProperty read(DataInput in) throws IOException { + public static CatalogLog read(DataInput in) throws IOException { String json = Text.readString(in); - return GsonUtils.GSON.fromJson(json, DataSourceProperty.class); + return GsonUtils.GSON.fromJson(json, CatalogLog.class); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceIf.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceIf.java index fa1ba1352b..4ac11d2480 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceIf.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceIf.java @@ -23,11 +23,12 @@ import org.apache.doris.common.DdlException; import org.apache.doris.common.MetaNotFoundException; import java.util.List; +import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; /** - * + * The interface of DataSource(catalog). */ public interface DataSourceIf { @@ -66,4 +67,10 @@ public interface DataSourceIf { DatabaseIf getDbOrAnalysisException(String dbName) throws AnalysisException; DatabaseIf getDbOrAnalysisException(long dbId) throws AnalysisException; + + Map<String, String> getProperties(); + + void modifyDatasourceName(String name); + + void modifyDatasourceProps(Map<String, String> props); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceMgr.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceMgr.java index 4d5c5be1c1..1192eb2b22 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceMgr.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceMgr.java @@ -17,11 +17,22 @@ package org.apache.doris.datasource; -import org.apache.doris.common.Config; -import org.apache.doris.common.MetaNotFoundException; +import org.apache.doris.analysis.AlterCatalogNameStmt; +import org.apache.doris.analysis.AlterCatalogPropertyStmt; +import org.apache.doris.analysis.CreateCatalogStmt; +import org.apache.doris.analysis.DropCatalogStmt; +import org.apache.doris.analysis.ShowCatalogStmt; +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.UserException; +import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; +import org.apache.doris.persist.OperationType; +import org.apache.doris.persist.gson.GsonUtils; +import org.apache.doris.qe.ShowResultSet; -import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -29,18 +40,22 @@ import org.apache.logging.log4j.Logger; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.List; import java.util.Map; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * DataSourceMgr will loaded all data sources at FE startup, - * and save them in maps mapping with id and name. + * DataSourceMgr will load all data sources at FE startup, + * and save them in map with name. + * Note: Catalog in sql syntax will be treated as datasource interface in code level. + * TODO: Change the package name into catalog. */ public class DataSourceMgr implements Writable { private static final Logger LOG = LogManager.getLogger(DataSourceMgr.class); - private Map<Long, DataSourceIf> idToDataSource = Maps.newConcurrentMap(); - private Map<String, DataSourceIf> nameToDataSource = Maps.newConcurrentMap(); - private DataSourceMgrProperty dsMgrProperty = new DataSourceMgrProperty(); + private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + + private final Map<String, DataSourceIf> nameToCatalogs = Maps.newConcurrentMap(); // Use a separate instance to facilitate access. // internalDataSource still exists in idToDataSource and nameToDataSource @@ -52,103 +67,182 @@ public class DataSourceMgr implements Writable { private void initInternalDataSource() { internalDataSource = new InternalDataSource(); - idToDataSource.put(internalDataSource.getId(), internalDataSource); - nameToDataSource.put(internalDataSource.getName(), internalDataSource); - } - - private void registerNewDataSource(ExternalDataSource ds) { - // TODO + nameToCatalogs.put(internalDataSource.getName(), internalDataSource); } public InternalDataSource getInternalDataSource() { return internalDataSource; } + private void writeLock() { + lock.writeLock().lock(); + } + + private void writeUnlock() { + lock.writeLock().unlock(); + } + + private void readLock() { + lock.readLock().lock(); + } + + private void readUnlock() { + lock.readLock().unlock(); + } + /** - * get data source by id. - * - * @param id - * @param e - * @param <E> - * @return - * @throws E + * Create and hold the catalog instance and write the meta log. */ - public <E extends MetaNotFoundException> DataSourceIf getDataSourceOrException(long id, - java.util.function.Function<Long, E> e) throws E { - DataSourceIf ds = idToDataSource.get(id); - if (ds == null) { - throw e.apply(id); + public void createCatalog(CreateCatalogStmt stmt) throws UserException { + if (stmt.isSetIfNotExists() && nameToCatalogs.containsKey(stmt.getCatalogName())) { + LOG.warn("Catalog {} is already exist.", stmt.getCatalogName()); + return; } - return ds; + if (nameToCatalogs.containsKey(stmt.getCatalogName())) { + throw new DdlException("Catalog had already exist with name: " + stmt.getCatalogName()); + } + CatalogLog log = CatalogFactory.constructorCatalogLog(stmt); + replayCreateCatalog(log); + Catalog.getCurrentCatalog().getEditLog().logDatasourceLog(OperationType.OP_CREATE_DS, log); } /** - * get data source by name. - * - * @param name - * @param e - * @param <E> - * @return - * @throws E + * Remove the catalog instance by name and write the meta log. */ - public <E extends MetaNotFoundException> DataSourceIf getDataSourceOrException(String name, - java.util.function.Function<String, E> e) throws E { - DataSourceIf ds = nameToDataSource.get(name); - if (ds == null) { - throw e.apply(name); + public void dropCatalog(DropCatalogStmt stmt) throws UserException { + if (stmt.isSetIfExists() && !nameToCatalogs.containsKey(stmt.getCatalogName())) { + LOG.warn("Non catalog {} is found.", stmt.getCatalogName()); + return; + } + if (!nameToCatalogs.containsKey(stmt.getCatalogName())) { + throw new DdlException("No catalog found with name: " + stmt.getCatalogName()); } - return ds; + CatalogLog log = CatalogFactory.constructorCatalogLog(stmt); + replayDropCatalog(log); + Catalog.getCurrentCatalog().getEditLog().logDatasourceLog(OperationType.OP_DROP_DS, log); } - public boolean hasDataSource(String name) { - return nameToDataSource.containsKey(name); + /** + * Modify the catalog name into a new one and write the meta log. + */ + public void alterCatalogName(AlterCatalogNameStmt stmt) throws UserException { + if (!nameToCatalogs.containsKey(stmt.getCatalogName())) { + throw new DdlException("No catalog found with name: " + stmt.getCatalogName()); + } + CatalogLog log = CatalogFactory.constructorCatalogLog(stmt); + replayAlterCatalogName(log); + Catalog.getCurrentCatalog().getEditLog().logDatasourceLog(OperationType.OP_ALTER_DS_NAME, log); } - @Override - public void write(DataOutput out) throws IOException { - if (Config.disable_cluster_feature) { - return; + /** + * Modify the catalog property and write the meta log. + */ + public void alterCatalogProps(AlterCatalogPropertyStmt stmt) throws UserException { + if (!nameToCatalogs.containsKey(stmt.getCatalogName())) { + throw new DdlException("No catalog found with name: " + stmt.getCatalogName()); } - Preconditions.checkState(false, "Do not call this until multi catalog feature is ready"); - int size = idToDataSource.size(); - if (idToDataSource.get(InternalDataSource.INTERNAL_DS_ID) != null) { - // No need to persis internal data source - size -= 1; + if (!nameToCatalogs.get(stmt.getCatalogName()) + .getType().equalsIgnoreCase(stmt.getNewProperties().get("type"))) { + throw new DdlException("Can't modify the type of catalog property with name: " + stmt.getCatalogName()); } - out.writeInt(size); - for (DataSourceIf ds : idToDataSource.values()) { - if (ds.getId() == InternalDataSource.INTERNAL_DS_ID) { - continue; + CatalogLog log = CatalogFactory.constructorCatalogLog(stmt); + replayAlterCatalogProps(log); + Catalog.getCurrentCatalog().getEditLog().logDatasourceLog(OperationType.OP_ALTER_DS_PROPS, log); + } + + /** + * List all catalog or get the special catalog with a name. + */ + public ShowResultSet showCatalogs(ShowCatalogStmt showStmt) throws AnalysisException { + List<List<String>> rows = Lists.newArrayList(); + readLock(); + try { + if (showStmt.getCatalogName() == null) { + for (DataSourceIf ds : nameToCatalogs.values()) { + List<String> row = Lists.newArrayList(); + row.add(ds.getName()); + row.add(ds.getType()); + rows.add(row); + } + } else { + if (!nameToCatalogs.containsKey(showStmt.getCatalogName())) { + throw new AnalysisException("No catalog found with name: " + showStmt.getCatalogName()); + } + DataSourceIf ds = nameToCatalogs.get(showStmt.getCatalogName()); + for (Map.Entry<String, String> elem : ds.getProperties().entrySet()) { + List<String> row = Lists.newArrayList(); + row.add(elem.getKey()); + row.add(elem.getValue()); + rows.add(row); + } } - ExternalDataSource extDs = (ExternalDataSource) ds; - extDs.write(out); + } finally { + readUnlock(); } - dsMgrProperty.write(out); + + return new ShowResultSet(showStmt.getMetaData(), rows); } /** - * read from image. - * - * @param in - * @return - * @throws IOException + * Reply for create catalog event. */ - public static DataSourceMgr read(DataInput in) throws IOException { - if (Config.disable_cluster_feature) { - return null; + public void replayCreateCatalog(CatalogLog log) { + writeLock(); + try { + DataSourceIf ds = CatalogFactory.constructorFromLog(log); + nameToCatalogs.put(ds.getName(), ds); + } finally { + writeUnlock(); + } + } + + /** + * Reply for drop catalog event. + */ + public void replayDropCatalog(CatalogLog log) { + writeLock(); + try { + nameToCatalogs.remove(log.getCatalogName()); + } finally { + writeUnlock(); + } + } + + /** + * Reply for alter catalog name event. + */ + public void replayAlterCatalogName(CatalogLog log) { + writeLock(); + try { + DataSourceIf ds = nameToCatalogs.remove(log.getCatalogName()); + ds.modifyDatasourceName(log.getNewCatalogName()); + nameToCatalogs.put(ds.getName(), ds); + } finally { + writeUnlock(); } - DataSourceMgr mgr = new DataSourceMgr(); - mgr.readFields(in); - return mgr; } - private void readFields(DataInput in) throws IOException { - int size = in.readInt(); - for (int i = 0; i < size; ++i) { - ExternalDataSource extDs = ExternalDataSource.read(in); - idToDataSource.put(extDs.getId(), extDs); - nameToDataSource.put(extDs.getName(), extDs); + /** + * Reply for alter catalog props event. + */ + public void replayAlterCatalogProps(CatalogLog log) { + writeLock(); + try { + DataSourceIf ds = nameToCatalogs.remove(log.getCatalogName()); + ds.modifyDatasourceProps(log.getNewProps()); + nameToCatalogs.put(ds.getName(), ds); + } finally { + writeUnlock(); } - dsMgrProperty = DataSourceMgrProperty.read(in); + } + + @Override + public void write(DataOutput out) throws IOException { + Text.writeString(out, GsonUtils.GSON.toJson(this)); + } + + public static DataSourceMgr read(DataInput in) throws IOException { + String json = Text.readString(in); + return GsonUtils.GSON.fromJson(json, DataSourceMgr.class); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java index 43bc87fb7d..b9d436ec5f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/DataSourceProperty.java @@ -23,12 +23,17 @@ import org.apache.doris.persist.gson.GsonUtils; import com.google.common.collect.Maps; import com.google.gson.annotations.SerializedName; +import lombok.Data; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.Map; +/** + * DataSourceProperty to store the properties for datasource. + */ +@Data public class DataSourceProperty implements Writable { @SerializedName(value = "properties") private Map<String, String> properties = Maps.newHashMap(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/EsExternalDataSource.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/EsExternalDataSource.java index 58a709e3c6..41909b0927 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/EsExternalDataSource.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/EsExternalDataSource.java @@ -18,12 +18,22 @@ package org.apache.doris.datasource; import java.util.List; +import java.util.Map; /** * External data source for elasticsearch */ public class EsExternalDataSource extends ExternalDataSource { + /** + * Default constructor for EsExternalDataSource. + */ + public EsExternalDataSource(String name, Map<String, String> props) { + setName(name); + getDsProperty().setProperties(props); + setType("es"); + } + @Override public List<String> listDatabaseNames(SessionContext ctx) { return null; diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDataSource.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDataSource.java index 67b27fbd38..7e94e90a30 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDataSource.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDataSource.java @@ -17,7 +17,6 @@ package org.apache.doris.datasource; -import org.apache.commons.lang.NotImplementedException; import org.apache.doris.catalog.DatabaseIf; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.DdlException; @@ -27,18 +26,22 @@ import org.apache.doris.common.io.Writable; import org.apache.doris.persist.gson.GsonUtils; import com.google.gson.annotations.SerializedName; +import lombok.Data; +import org.apache.commons.lang.NotImplementedException; import org.jetbrains.annotations.Nullable; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; /** * The abstract class for all types of external data sources. */ +@Data public abstract class ExternalDataSource implements DataSourceIf, Writable { // Unique id of this data source, will be assigned after data source is loaded. @SerializedName(value = "id") @@ -153,6 +156,21 @@ public abstract class ExternalDataSource implements DataSourceIf, Writable { throw new NotImplementedException(); } + @Override + public Map<String, String> getProperties() { + return dsProperty.getProperties(); + } + + @Override + public void modifyDatasourceName(String name) { + this.name = name; + } + + @Override + public void modifyDatasourceProps(Map<String, String> props) { + dsProperty.setProperties(props); + } + @Override public void write(DataOutput out) throws IOException { Text.writeString(out, GsonUtils.GSON.toJson(this)); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/HMSExternalDataSource.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/HMSExternalDataSource.java index 6de84387dd..fa7b27d5a3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/HMSExternalDataSource.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/HMSExternalDataSource.java @@ -53,14 +53,22 @@ public class HMSExternalDataSource extends ExternalDataSource { protected String hiveMetastoreUris; protected HiveMetaStoreClient client; + /** + * Default constructor for HMSExternalDataSource. + */ + public HMSExternalDataSource(String name, Map<String, String> props) { + setName(name); + getDsProperty().setProperties(props); + setType("hms"); + } + /** * Hive metastore data source implementation. * * @param hiveMetastoreUris e.g. thrift://127.0.0.1:9083 */ public HMSExternalDataSource(long id, String name, String type, DataSourceProperty dsProperty, - String hiveMetastoreUris) - throws DdlException { + String hiveMetastoreUris) throws DdlException { this.id = id; this.name = name; this.type = type; @@ -186,37 +194,37 @@ public class HMSExternalDataSource extends ExternalDataSource { @Override public DatabaseIf getDbOrMetaException(String dbName) throws MetaNotFoundException { - return getDbOrException(dbName, s -> new MetaNotFoundException("unknown databases, dbName=" + s, - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbName, + s -> new MetaNotFoundException("unknown databases, dbName=" + s, ErrorCode.ERR_BAD_DB_ERROR)); } @Override public DatabaseIf getDbOrMetaException(long dbId) throws MetaNotFoundException { - return getDbOrException(dbId, s -> new MetaNotFoundException("unknown databases, dbId=" + s, - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbId, + s -> new MetaNotFoundException("unknown databases, dbId=" + s, ErrorCode.ERR_BAD_DB_ERROR)); } @Override public DatabaseIf getDbOrDdlException(String dbName) throws DdlException { - return getDbOrException(dbName, s -> new DdlException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbName, + s -> new DdlException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), ErrorCode.ERR_BAD_DB_ERROR)); } @Override public DatabaseIf getDbOrDdlException(long dbId) throws DdlException { - return getDbOrException(dbId, s -> new DdlException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbId, + s -> new DdlException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), ErrorCode.ERR_BAD_DB_ERROR)); } @Override public DatabaseIf getDbOrAnalysisException(String dbName) throws AnalysisException { - return getDbOrException(dbName, s -> new AnalysisException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbName, + s -> new AnalysisException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), ErrorCode.ERR_BAD_DB_ERROR)); } @Override public DatabaseIf getDbOrAnalysisException(long dbId) throws AnalysisException { - return getDbOrException(dbId, s -> new AnalysisException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), - ErrorCode.ERR_BAD_DB_ERROR)); + return getDbOrException(dbId, + s -> new AnalysisException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), ErrorCode.ERR_BAD_DB_ERROR)); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalDataSource.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalDataSource.java index 5e32f2f418..8ea01770ca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalDataSource.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalDataSource.java @@ -326,6 +326,21 @@ public class InternalDataSource implements DataSourceIf { s -> new AnalysisException(ErrorCode.ERR_BAD_DB_ERROR.formatErrorMsg(s), ErrorCode.ERR_BAD_DB_ERROR)); } + @Override + public Map<String, String> getProperties() { + return Maps.newHashMap(); + } + + @Override + public void modifyDatasourceName(String name) { + LOG.warn("Ignore the modify datasource name in build-in datasource."); + } + + @Override + public void modifyDatasourceProps(Map<String, String> props) { + LOG.warn("Ignore the modify datasource props in build-in datasource."); + } + // Use tryLock to avoid potential dead lock private boolean tryLock(boolean mustLock) { while (true) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java index 5cf3466a9b..78d200b9f7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java +++ b/fe/fe-core/src/main/java/org/apache/doris/journal/JournalEntity.java @@ -36,6 +36,7 @@ import org.apache.doris.cluster.Cluster; import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; import org.apache.doris.common.util.SmallFileMgr.SmallFile; +import org.apache.doris.datasource.CatalogLog; import org.apache.doris.ha.MasterInfo; import org.apache.doris.journal.bdbje.Timestamp; import org.apache.doris.load.DeleteInfo; @@ -653,6 +654,14 @@ public class JournalEntity implements Writable { isRead = true; break; } + case OperationType.OP_CREATE_DS: + case OperationType.OP_DROP_DS: + case OperationType.OP_ALTER_DS_NAME: + case OperationType.OP_ALTER_DS_PROPS: { + data = CatalogLog.read(in); + isRead = true; + break; + } default: { IOException e = new IOException(); LOG.error("UNKNOWN Operation Type {}", opCode, e); diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java index ca958128ab..219143957a 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/persist/EditLog.java @@ -41,6 +41,7 @@ import org.apache.doris.common.MetaNotFoundException; import org.apache.doris.common.io.Text; import org.apache.doris.common.io.Writable; import org.apache.doris.common.util.SmallFileMgr.SmallFile; +import org.apache.doris.datasource.CatalogLog; import org.apache.doris.ha.MasterInfo; import org.apache.doris.journal.Journal; import org.apache.doris.journal.JournalCursor; @@ -821,6 +822,26 @@ public class EditLog { catalog.getPolicyMgr().replayDrop(log); break; } + case OperationType.OP_CREATE_DS: { + CatalogLog log = (CatalogLog) journal.getData(); + catalog.getDataSourceMgr().replayCreateCatalog(log); + break; + } + case OperationType.OP_DROP_DS: { + CatalogLog log = (CatalogLog) journal.getData(); + catalog.getDataSourceMgr().replayDropCatalog(log); + break; + } + case OperationType.OP_ALTER_DS_NAME: { + CatalogLog log = (CatalogLog) journal.getData(); + catalog.getDataSourceMgr().replayAlterCatalogName(log); + break; + } + case OperationType.OP_ALTER_DS_PROPS: { + CatalogLog log = (CatalogLog) journal.getData(); + catalog.getDataSourceMgr().replayAlterCatalogProps(log); + break; + } default: { IOException e = new IOException(); LOG.error("UNKNOWN Operation Type {}", opCode, e); @@ -1431,4 +1452,8 @@ public class EditLog { public void logDropPolicy(DropPolicyLog log) { logEdit(OperationType.OP_DROP_POLICY, log); } + + public void logDatasourceLog(short id, CatalogLog log) { + logEdit(id, log); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java index fbdf117921..79d3cf63a0 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/persist/OperationType.java @@ -223,6 +223,12 @@ public class OperationType { public static final short OP_CREATE_POLICY = 310; public static final short OP_DROP_POLICY = 311; + // datasource 312-315 + public static final short OP_CREATE_DS = 312; + public static final short OP_DROP_DS = 313; + public static final short OP_ALTER_DS_NAME = 314; + public static final short OP_ALTER_DS_PROPS = 315; + // get opcode name by op codeStri public static String getOpName(short opCode) { try { diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java index 16e9de59e0..ded0478baa 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java +++ b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/MetaPersistMethod.java @@ -195,6 +195,12 @@ public class MetaPersistMethod { metaPersistMethod.writeMethod = Catalog.class.getDeclaredMethod("savePolicy", CountingDataOutputStream.class, long.class); break; + case "datasource": + metaPersistMethod.readMethod = + Catalog.class.getDeclaredMethod("loadDatasource", DataInputStream.class, long.class); + metaPersistMethod.writeMethod = + Catalog.class.getDeclaredMethod("saveDatasource", CountingDataOutputStream.class, long.class); + break; default: break; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java index 6e01f13af9..920583bab7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java +++ b/fe/fe-core/src/main/java/org/apache/doris/persist/meta/PersistMetaModules.java @@ -38,7 +38,7 @@ public class PersistMetaModules { new String[] {"masterInfo", "frontends", "backends", "db", "loadJob", "alterJob", "recycleBin", "globalVariable", "cluster", "broker", "resources", "exportJob", "syncJob", "backupHandler", "paloAuth", "transactionState", "colocateTableIndex", "routineLoadJobs", "loadJobV2", "smallFiles", - "plugins", "deleteHandler", "sqlBlockRule", "policy"}); + "plugins", "deleteHandler", "sqlBlockRule", "policy", "datasource"}); static { MODULES_MAP = Maps.newHashMap(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java index e369463ad5..6bf4b07ea9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/DdlExecutor.java @@ -26,6 +26,8 @@ import org.apache.doris.analysis.AdminRebalanceDiskStmt; import org.apache.doris.analysis.AdminRepairTableStmt; import org.apache.doris.analysis.AdminSetConfigStmt; import org.apache.doris.analysis.AdminSetReplicaStatusStmt; +import org.apache.doris.analysis.AlterCatalogNameStmt; +import org.apache.doris.analysis.AlterCatalogPropertyStmt; import org.apache.doris.analysis.AlterClusterStmt; import org.apache.doris.analysis.AlterColumnStatsStmt; import org.apache.doris.analysis.AlterDatabasePropertyStmt; @@ -44,6 +46,7 @@ import org.apache.doris.analysis.CancelAlterSystemStmt; import org.apache.doris.analysis.CancelAlterTableStmt; import org.apache.doris.analysis.CancelBackupStmt; import org.apache.doris.analysis.CancelLoadStmt; +import org.apache.doris.analysis.CreateCatalogStmt; import org.apache.doris.analysis.CreateClusterStmt; import org.apache.doris.analysis.CreateDataSyncJobStmt; import org.apache.doris.analysis.CreateDbStmt; @@ -64,6 +67,7 @@ import org.apache.doris.analysis.CreateUserStmt; import org.apache.doris.analysis.CreateViewStmt; import org.apache.doris.analysis.DdlStmt; import org.apache.doris.analysis.DeleteStmt; +import org.apache.doris.analysis.DropCatalogStmt; import org.apache.doris.analysis.DropClusterStmt; import org.apache.doris.analysis.DropDbStmt; import org.apache.doris.analysis.DropEncryptKeyStmt; @@ -300,6 +304,14 @@ public class DdlExecutor { catalog.getPolicyMgr().createPolicy((CreatePolicyStmt) ddlStmt); } else if (ddlStmt instanceof DropPolicyStmt) { catalog.getPolicyMgr().dropPolicy((DropPolicyStmt) ddlStmt); + } else if (ddlStmt instanceof CreateCatalogStmt) { + catalog.getDataSourceMgr().createCatalog((CreateCatalogStmt) ddlStmt); + } else if (ddlStmt instanceof DropCatalogStmt) { + catalog.getDataSourceMgr().dropCatalog((DropCatalogStmt) ddlStmt); + } else if (ddlStmt instanceof AlterCatalogNameStmt) { + catalog.getDataSourceMgr().alterCatalogName((AlterCatalogNameStmt) ddlStmt); + } else if (ddlStmt instanceof AlterCatalogPropertyStmt) { + catalog.getDataSourceMgr().alterCatalogProps((AlterCatalogPropertyStmt) ddlStmt); } else { throw new DdlException("Unknown statement."); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index 580b79378e..ac5770956b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -30,6 +30,7 @@ import org.apache.doris.analysis.ShowAuthorStmt; import org.apache.doris.analysis.ShowBackendsStmt; import org.apache.doris.analysis.ShowBackupStmt; import org.apache.doris.analysis.ShowBrokerStmt; +import org.apache.doris.analysis.ShowCatalogStmt; import org.apache.doris.analysis.ShowClusterStmt; import org.apache.doris.analysis.ShowCollationStmt; import org.apache.doris.analysis.ShowColumnStatsStmt; @@ -346,6 +347,8 @@ public class ShowExecutor { handleShowCreateMaterializedView(); } else if (stmt instanceof ShowPolicyStmt) { handleShowPolicy(); + } else if (stmt instanceof ShowCatalogStmt) { + handleDatasource(); } else { handleEmtpy(); } @@ -2211,4 +2214,8 @@ public class ShowExecutor { resultSet = Catalog.getCurrentCatalog().getPolicyMgr().showPolicy(showStmt); } + public void handleDatasource() throws AnalysisException { + ShowCatalogStmt showStmt = (ShowCatalogStmt) stmt; + resultSet = Catalog.getCurrentCatalog().getDataSourceMgr().showCatalogs(showStmt); + } } diff --git a/fe/fe-core/src/main/jflex/sql_scanner.flex b/fe/fe-core/src/main/jflex/sql_scanner.flex index d57c6e8519..f7d3638c2a 100644 --- a/fe/fe-core/src/main/jflex/sql_scanner.flex +++ b/fe/fe-core/src/main/jflex/sql_scanner.flex @@ -433,6 +433,8 @@ import org.apache.doris.qe.SqlModeHelper; keywordMap.put("||", new Integer(SqlParserSymbols.KW_PIPE)); keywordMap.put("current_timestamp", new Integer(SqlParserSymbols.KW_CURRENT_TIMESTAMP)); keywordMap.put("not_null", new Integer(SqlParserSymbols.KW_NOT_NULL)); + keywordMap.put("catalog", new Integer(SqlParserSymbols.KW_CATALOG)); + keywordMap.put("catalogs", new Integer(SqlParserSymbols.KW_CATALOGS)); } // map from token id to token description diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogNameStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogNameStmtTest.java new file mode 100644 index 0000000000..c7f3e393d0 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogNameStmtTest.java @@ -0,0 +1,94 @@ +// 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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.apache.doris.qe.ConnectContext; + +import mockit.Mocked; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class AlterCatalogNameStmtTest { + private Analyzer analyzer; + @Mocked + private PaloAuth auth; + @Mocked + private ConnectContext ctx; + + @Before + public void setUp() throws DdlException { + Config.enable_multi_catalog = true; + analyzer = AccessTestUtil.fetchAdminAnalyzer(false); + MockedAuth.mockedAuth(auth); + MockedAuth.mockedConnectContext(ctx, "root", "127.0.0.1"); + } + + @Test + public void testNormalCase() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt("testCatalog", "testNewCatalog"); + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + Assert.assertEquals("testNewCatalog", stmt.getNewCatalogName()); + } + + @Test(expected = AnalysisException.class) + public void testEmptyDs1() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt("", "testNewCatalog"); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testEmptyDs2() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt("testCatalog", ""); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testBuildIn1() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt( + InternalDataSource.INTERNAL_DS_NAME, "testNewCatalog"); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testBuildIn2() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt( + "testCatalog", InternalDataSource.INTERNAL_DS_NAME); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testNameFormat() throws UserException { + AlterCatalogNameStmt stmt = new AlterCatalogNameStmt( + "testCatalog", InternalDataSource.INTERNAL_DS_NAME); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogPropsStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogPropsStmtTest.java new file mode 100644 index 0000000000..821d65498a --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/AlterCatalogPropsStmtTest.java @@ -0,0 +1,91 @@ +// 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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.collect.Maps; +import mockit.Mocked; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +public class AlterCatalogPropsStmtTest { + private Analyzer analyzer; + @Mocked + private PaloAuth auth; + @Mocked + private ConnectContext ctx; + + @Before + public void setUp() throws DdlException { + Config.enable_multi_catalog = true; + analyzer = AccessTestUtil.fetchAdminAnalyzer(false); + MockedAuth.mockedAuth(auth); + MockedAuth.mockedConnectContext(ctx, "root", "127.0.0.1"); + } + + @Test + public void testNormalCase() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + AlterCatalogPropertyStmt stmt = new AlterCatalogPropertyStmt("testCatalog", props); + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + Assert.assertEquals(2, stmt.getNewProperties().size()); + } + + @Test(expected = AnalysisException.class) + public void testName() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + AlterCatalogPropertyStmt stmt = new AlterCatalogPropertyStmt("", props); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testBuildIn() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + AlterCatalogPropertyStmt stmt = new AlterCatalogPropertyStmt(InternalDataSource.INTERNAL_DS_NAME, props); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } + + @Test(expected = AnalysisException.class) + public void testPropType() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + AlterCatalogPropertyStmt stmt = new AlterCatalogPropertyStmt(InternalDataSource.INTERNAL_DS_NAME, props); + stmt.analyze(analyzer); + Assert.fail("No exception throws."); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateCatalogStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateCatalogStmtTest.java new file mode 100644 index 0000000000..4d61e6c219 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/CreateCatalogStmtTest.java @@ -0,0 +1,93 @@ +// 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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.apache.doris.qe.ConnectContext; + +import com.google.common.collect.Maps; +import mockit.Mocked; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +public class CreateCatalogStmtTest { + private Analyzer analyzer; + + @Mocked + private PaloAuth auth; + @Mocked + private ConnectContext ctx; + + @Before() + public void setUp() throws DdlException { + Config.enable_multi_catalog = true; + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + MockedAuth.mockedAuth(auth); + MockedAuth.mockedConnectContext(ctx, "root", "127.0.0.1"); + } + + @Test + public void testAnalyzeNormal() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + CreateCatalogStmt stmt = new CreateCatalogStmt(false, "testCatalog", props); + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + Assert.assertNotNull(stmt.getProperties()); + Assert.assertEquals(2, stmt.getProperties().size()); + } + + @Test(expected = AnalysisException.class) + public void testAnalyzeWithException() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + CreateCatalogStmt stmt = new CreateCatalogStmt(false, "", props); + stmt.analyze(analyzer); + Assert.fail("no exception"); + } + + @Test(expected = AnalysisException.class) + public void testBuildInException() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("type", "hms"); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + CreateCatalogStmt stmt = new CreateCatalogStmt(false, InternalDataSource.INTERNAL_DS_NAME, props); + stmt.analyze(analyzer); + Assert.fail("no exception"); + } + + @Test(expected = AnalysisException.class) + public void testPropsTypeException() throws UserException { + Map<String, String> props = Maps.newHashMap(); + props.put("hive.metastore.uris", "thrift://localhost:9083"); + CreateCatalogStmt stmt = new CreateCatalogStmt(false, InternalDataSource.INTERNAL_DS_NAME, props); + stmt.analyze(analyzer); + Assert.fail("no exception"); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/DropCatalogStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/DropCatalogStmtTest.java new file mode 100644 index 0000000000..36357bfc7e --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/DropCatalogStmtTest.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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.DdlException; +import org.apache.doris.common.UserException; +import org.apache.doris.datasource.InternalDataSource; +import org.apache.doris.mysql.privilege.MockedAuth; +import org.apache.doris.mysql.privilege.PaloAuth; +import org.apache.doris.qe.ConnectContext; + +import mockit.Mocked; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +public class DropCatalogStmtTest { + Analyzer analyzer; + + @Mocked + private PaloAuth auth; + @Mocked + private ConnectContext ctx; + + @Before + public void setUp() throws DdlException { + Config.enable_multi_catalog = true; + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + MockedAuth.mockedAuth(auth); + MockedAuth.mockedConnectContext(ctx, "root", "127.0.0.1"); + } + + @Test + public void testNormal() throws UserException, AnalysisException { + DropCatalogStmt stmt = new DropCatalogStmt(false, "testCatalog"); + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + } + + @Test(expected = AnalysisException.class) + public void testBuildInName() throws UserException, AnalysisException { + DropCatalogStmt stmt = new DropCatalogStmt(false, InternalDataSource.INTERNAL_DS_NAME); + + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + } + + @Test(expected = AnalysisException.class) + public void testBadName() throws UserException, AnalysisException { + DropCatalogStmt stmt = new DropCatalogStmt(false, ""); + + stmt.analyze(analyzer); + Assert.assertEquals("testCatalog", stmt.getCatalogName()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowCatalogStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowCatalogStmtTest.java new file mode 100644 index 0000000000..d717b9d71a --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowCatalogStmtTest.java @@ -0,0 +1,44 @@ +// 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.doris.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.Config; +import org.apache.doris.common.UserException; + +import org.junit.Assert; +import org.junit.Test; + +public class ShowCatalogStmtTest { + @Test + public void testNormal() throws UserException, AnalysisException { + Config.enable_multi_catalog = true; + final Analyzer analyzer = AccessTestUtil.fetchBlockAnalyzer(); + ShowCatalogStmt stmt = new ShowCatalogStmt(); + stmt.analyze(analyzer); + Assert.assertNull(stmt.getCatalogName()); + Assert.assertEquals(2, stmt.getMetaData().getColumnCount()); + Assert.assertEquals("SHOW CATALOGS", stmt.toSql()); + + stmt = new ShowCatalogStmt("testCatalog"); + stmt.analyze(analyzer); + Assert.assertNotNull(stmt.getCatalogName()); + Assert.assertEquals(2, stmt.getMetaData().getColumnCount()); + Assert.assertEquals("SHOW CATALOG testCatalog", stmt.toSql()); + } +} diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/DatasourceMgrTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/DatasourceMgrTest.java new file mode 100644 index 0000000000..68636203b5 --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/DatasourceMgrTest.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.doris.datasource; + +import org.apache.doris.analysis.AlterCatalogNameStmt; +import org.apache.doris.analysis.AlterCatalogPropertyStmt; +import org.apache.doris.analysis.CreateCatalogStmt; +import org.apache.doris.analysis.DropCatalogStmt; +import org.apache.doris.analysis.ShowCatalogStmt; +import org.apache.doris.catalog.Catalog; +import org.apache.doris.common.Config; +import org.apache.doris.common.FeConstants; +import org.apache.doris.qe.ShowResultSet; +import org.apache.doris.utframe.TestWithFeService; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class DatasourceMgrTest extends TestWithFeService { + private DataSourceMgr mgr; + + @Override + protected void runBeforeAll() throws Exception { + Config.enable_multi_catalog = true; + FeConstants.runningUnitTest = true; + mgr = Catalog.getCurrentCatalog().getDataSourceMgr(); + } + + @Test + public void testNormalCase() throws Exception { + String createCatalogSql = "CREATE CATALOG hms_catalog " + + "properties( \"type\" = \"hms\", \"hive.metastore.uris\"=\"thrift://localhost:9083\" )"; + CreateCatalogStmt createStmt = (CreateCatalogStmt) parseAndAnalyzeStmt(createCatalogSql); + mgr.createCatalog(createStmt); + + String showCatalogSql = "SHOW CATALOGS"; + ShowCatalogStmt showStmt = (ShowCatalogStmt) parseAndAnalyzeStmt(showCatalogSql); + ShowResultSet showResultSet = mgr.showCatalogs(showStmt); + Assertions.assertEquals(2, showResultSet.getResultRows().size()); + + String alterCatalogNameSql = "ALTER CATALOG hms_catalog RENAME my_catalog;"; + AlterCatalogNameStmt alterNameStmt = (AlterCatalogNameStmt) parseAndAnalyzeStmt(alterCatalogNameSql); + mgr.alterCatalogName(alterNameStmt); + + String alterCatalogProps = "ALTER CATALOG my_catalog SET PROPERTIES" + + " (\"type\" = \"hms\", \"k\" = \"v\");"; + AlterCatalogPropertyStmt alterPropStmt = (AlterCatalogPropertyStmt) parseAndAnalyzeStmt(alterCatalogProps); + mgr.alterCatalogProps(alterPropStmt); + + showResultSet = mgr.showCatalogs(showStmt); + for (List<String> row : showResultSet.getResultRows()) { + if (row.get(1).equals("internal")) { + continue; + } + Assertions.assertEquals("my_catalog", row.get(0)); + } + + String showDetailCatalog = "SHOW CATALOG my_catalog"; + ShowCatalogStmt showDetailStmt = (ShowCatalogStmt) parseAndAnalyzeStmt(showDetailCatalog); + showResultSet = mgr.showCatalogs(showDetailStmt); + + for (List<String> row : showResultSet.getResultRows()) { + Assertions.assertEquals(2, row.size()); + if (row.get(0).equalsIgnoreCase("type")) { + Assertions.assertEquals("hms", row.get(1)); + } else if (row.get(0).equalsIgnoreCase("k")) { + Assertions.assertEquals("v", row.get(1)); + } else { + Assertions.fail(); + } + } + + String dropCatalogSql = "DROP CATALOG my_catalog"; + DropCatalogStmt dropCatalogStmt = (DropCatalogStmt) parseAndAnalyzeStmt(dropCatalogSql); + mgr.dropCatalog(dropCatalogStmt); + showResultSet = mgr.showCatalogs(showStmt); + Assertions.assertEquals(1, showResultSet.getResultRows().size()); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org