From 1be753ed7544a0f709907e9ceffe8be190fb1840 Mon Sep 17 00:00:00 2001 From: zy-kkk Date: Tue, 2 Apr 2024 22:56:14 +0800 Subject: [PATCH] [enhancement](mysql compatible) add user and procs_priv tables to mysql db in all catalogs (#33058) Issue Number: close #xxx This PR aims to enhance the compatibility of BI tools (such as Dbeaver, DataGrip) when using the mysql connector to connect to Doris, because some BI tools query some tables in the mysql database. In our tests, the user and procs_priv tables were mainly queried. This PR adds these two tables and adds actual data to the user table. However, please note that most of the fields in the user table are in Doris' own format rather than mysql format, so it can only ensure that the BI tool is querying No error is reported when accessing these tables, which does not guarantee that the data is completely displayed, and the tables under Doris's mysql database do not support data modification. Thanks to @liujiwen-up for assisting in testing --- be/src/exec/schema_scanner.cpp | 5 + be/src/exec/schema_scanner/schema_helper.cpp | 10 ++ be/src/exec/schema_scanner/schema_helper.h | 4 + .../schema_scanner/schema_user_scanner.cpp | 134 ++++++++++++++++++ .../exec/schema_scanner/schema_user_scanner.h | 53 +++++++ .../apache/doris/analysis/RefreshDbStmt.java | 6 + .../doris/analysis/SchemaTableType.java | 4 +- .../apache/doris/catalog/MysqlDBTable.java | 98 +++++++++++++ .../org/apache/doris/catalog/MysqlDb.java | 6 +- .../doris/datasource/ExternalCatalog.java | 12 +- .../doris/datasource/ExternalDatabase.java | 28 +++- .../infoschema/ExternalMysqlDatabase.java | 55 +++++++ .../infoschema/ExternalMysqlTable.java | 51 +++++++ .../datasource/jdbc/client/JdbcClient.java | 2 + .../jdbc/client/JdbcMySQLClient.java | 2 + .../apache/doris/mysql/privilege/Auth.java | 94 ++++++++++++ .../apache/doris/persist/gson/GsonUtils.java | 4 + .../doris/service/FrontendServiceImpl.java | 9 ++ .../doris/catalog/RefreshTableTest.java | 11 ++ .../doris/datasource/RefreshCatalogTest.java | 14 +- .../service/FrontendServiceImplTest.java | 10 ++ gensrc/thrift/Descriptors.thrift | 4 +- gensrc/thrift/FrontendService.thrift | 8 ++ .../jdbc/test_clickhouse_jdbc_catalog.out | Bin 5487 -> 5493 bytes .../jdbc/test_db2_jdbc_catalog.out | 1 + .../jdbc/test_mariadb_jdbc_catalog.out | 2 +- .../jdbc/test_mysql_jdbc_catalog.out | 7 +- .../jdbc/test_mysql_jdbc_driver5_catalog.out | 9 +- .../jdbc/test_oracle_jdbc_catalog.out | 3 + .../jdbc/test_pg_jdbc_catalog.out | 5 + .../jdbc/test_sqlserver_jdbc_catalog.out | 2 + 31 files changed, 634 insertions(+), 19 deletions(-) create mode 100644 be/src/exec/schema_scanner/schema_user_scanner.cpp create mode 100644 be/src/exec/schema_scanner/schema_user_scanner.h create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDBTable.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlDatabase.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlTable.java diff --git a/be/src/exec/schema_scanner.cpp b/be/src/exec/schema_scanner.cpp index 32197e37e1e21b..87995a3f0ef015 100644 --- a/be/src/exec/schema_scanner.cpp +++ b/be/src/exec/schema_scanner.cpp @@ -43,6 +43,7 @@ #include "exec/schema_scanner/schema_table_privileges_scanner.h" #include "exec/schema_scanner/schema_tables_scanner.h" #include "exec/schema_scanner/schema_user_privileges_scanner.h" +#include "exec/schema_scanner/schema_user_scanner.h" #include "exec/schema_scanner/schema_variables_scanner.h" #include "exec/schema_scanner/schema_views_scanner.h" #include "exec/schema_scanner/schema_workload_groups_scanner.h" @@ -161,6 +162,10 @@ std::unique_ptr SchemaScanner::create(TSchemaTableType::type type return SchemaWorkloadGroupsScanner::create_unique(); case TSchemaTableType::SCH_PROCESSLIST: return SchemaProcessListScanner::create_unique(); + case TSchemaTableType::SCH_PROCEDURES: + return SchemaRoutinesScanner::create_unique(); + case TSchemaTableType::SCH_USER: + return SchemaUserScanner::create_unique(); default: return SchemaDummyScanner::create_unique(); break; diff --git a/be/src/exec/schema_scanner/schema_helper.cpp b/be/src/exec/schema_scanner/schema_helper.cpp index 965dc7a6eb4556..2819dc603f70d0 100644 --- a/be/src/exec/schema_scanner/schema_helper.cpp +++ b/be/src/exec/schema_scanner/schema_helper.cpp @@ -37,6 +37,8 @@ class TShowVariableRequest; class TShowVariableResult; class TShowProcessListRequest; class TShowProcessListResult; +class TShowUserRequest; +class TShowUserResult; Status SchemaHelper::get_db_names(const std::string& ip, const int32_t port, const TGetDbsParams& request, TGetDbsResult* result) { @@ -134,4 +136,12 @@ Status SchemaHelper::show_process_list(const std::string& ip, const int32_t port }); } +Status SchemaHelper::show_user(const std::string& ip, const int32_t port, + const TShowUserRequest& request, TShowUserResult* result) { + return ThriftRpcHelper::rpc( + ip, port, [&request, &result](FrontendServiceConnection& client) { + client->showUser(*result, request); + }); +} + } // namespace doris diff --git a/be/src/exec/schema_scanner/schema_helper.h b/be/src/exec/schema_scanner/schema_helper.h index 5602e3c005f081..752e282bb52f2a 100644 --- a/be/src/exec/schema_scanner/schema_helper.h +++ b/be/src/exec/schema_scanner/schema_helper.h @@ -39,6 +39,8 @@ class TShowVariableRequest; class TShowVariableResult; class TShowProcessListRequest; class TShowProcessListResult; +class TShowUserRequest; +class TShowUserResult; // this class is a helper for getting schema info from FE class SchemaHelper { @@ -82,6 +84,8 @@ class SchemaHelper { static Status show_process_list(const std::string& ip, const int32_t port, const TShowProcessListRequest& request, TShowProcessListResult* result); + static Status show_user(const std::string& ip, const int32_t port, + const TShowUserRequest& request, TShowUserResult* result); }; } // namespace doris diff --git a/be/src/exec/schema_scanner/schema_user_scanner.cpp b/be/src/exec/schema_scanner/schema_user_scanner.cpp new file mode 100644 index 00000000000000..9b153414380350 --- /dev/null +++ b/be/src/exec/schema_scanner/schema_user_scanner.cpp @@ -0,0 +1,134 @@ +// 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. + +#include "exec/schema_scanner/schema_user_scanner.h" + +#include + +#include + +#include "exec/schema_scanner/schema_helper.h" +#include "runtime/runtime_state.h" +#include "vec/common/string_ref.h" +#include "vec/core/block.h" +#include "vec/data_types/data_type_factory.hpp" + +namespace doris { + +std::vector SchemaUserScanner::_s_user_columns = { + {"Host", TYPE_CHAR, sizeof(StringRef), false}, + {"User", TYPE_CHAR, sizeof(StringRef), false}, + {"Node_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Admin_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Grant_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Select_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Load_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Alter_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Create_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Drop_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Usage_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Show_view_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Cluster_usage_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"Stage_usage_priv", TYPE_CHAR, sizeof(StringRef), false}, + {"ssl_type", TYPE_CHAR, sizeof(StringRef), false}, + {"ssl_cipher", TYPE_VARCHAR, sizeof(StringRef), false}, + {"x509_issuer", TYPE_VARCHAR, sizeof(StringRef), false}, + {"x509_subject", TYPE_VARCHAR, sizeof(StringRef), false}, + {"max_questions", TYPE_BIGINT, sizeof(int64_t), false}, + {"max_updates", TYPE_BIGINT, sizeof(int64_t), false}, + {"max_connections", TYPE_BIGINT, sizeof(int64_t), false}, + {"max_user_connections", TYPE_BIGINT, sizeof(int64_t), false}, + {"plugin", TYPE_CHAR, sizeof(StringRef), false}, + {"authentication_string", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.expiration_seconds", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.password_creation_time", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.history_num", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.history_passwords", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.num_failed_login", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.password_lock_seconds", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.failed_login_counter", TYPE_VARCHAR, sizeof(StringRef), false}, + {"password_policy.lock_time", TYPE_VARCHAR, sizeof(StringRef), false}}; + +SchemaUserScanner::SchemaUserScanner() + : SchemaScanner(_s_user_columns, TSchemaTableType::SCH_USER) {} + +SchemaUserScanner::~SchemaUserScanner() = default; + +Status SchemaUserScanner::start(RuntimeState* state) { + TShowUserRequest request; + RETURN_IF_ERROR(SchemaHelper::show_user(*(_param->common_param->ip), _param->common_param->port, + request, &_user_result)); + + return Status::OK(); +} + +Status SchemaUserScanner::get_next_block(vectorized::Block* block, bool* eos) { + if (!_is_init) { + return Status::InternalError("call this before initial."); + } + if (block == nullptr || eos == nullptr) { + return Status::InternalError("invalid parameter."); + } + + *eos = true; + if (_user_result.userinfo_list.empty()) { + return Status::OK(); + } + + return _fill_block_impl(block); +} + +Status SchemaUserScanner::_fill_block_impl(vectorized::Block* block) { + SCOPED_TIMER(_fill_block_timer); + + const auto& userinfo_list = _user_result.userinfo_list; + size_t row_num = userinfo_list.size(); + if (row_num == 0) { + return Status::OK(); + } + + for (size_t col_idx = 0; col_idx < _s_user_columns.size(); ++col_idx) { + std::vector str_refs(row_num); + std::vector int_vals(row_num); + std::vector datas(row_num); + std::vector column_values( + row_num); // Store the strings to ensure their lifetime + + for (size_t row_idx = 0; row_idx < row_num; ++row_idx) { + const auto& row = userinfo_list[row_idx]; + std::string column_value = row.size() > col_idx ? row[col_idx] : ""; + + // Ensure column value assignment and type casting + column_values[row_idx] = column_value; + if (_s_user_columns[col_idx].type == TYPE_BIGINT) { + int64_t val = !column_value.empty() ? std::stoll(column_value) : 0; + int_vals[row_idx] = val; + datas[row_idx] = &int_vals[row_idx]; + } else { + str_refs[row_idx] = + StringRef(column_values[row_idx].data(), column_values[row_idx].size()); + datas[row_idx] = &str_refs[row_idx]; + } + } + + RETURN_IF_ERROR(fill_dest_column_for_range(block, col_idx, datas)); + } + + return Status::OK(); +} + +} // namespace doris diff --git a/be/src/exec/schema_scanner/schema_user_scanner.h b/be/src/exec/schema_scanner/schema_user_scanner.h new file mode 100644 index 00000000000000..c55f216804d5dd --- /dev/null +++ b/be/src/exec/schema_scanner/schema_user_scanner.h @@ -0,0 +1,53 @@ +// 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. + +#pragma once + +#include +#include + +#include "common/status.h" +#include "exec/schema_scanner.h" +#include "gen_cpp/FrontendService_types.h" + +namespace doris { + +class RuntimeState; + +namespace vectorized { +class Block; +} + +class SchemaUserScanner : public SchemaScanner { + ENABLE_FACTORY_CREATOR(SchemaUserScanner); + +public: + SchemaUserScanner(); + ~SchemaUserScanner() override; + + Status start(RuntimeState* state) override; + Status get_next_block(vectorized::Block* block, bool* eos) override; + + static std::vector _s_user_columns; + +private: + Status _fill_block_impl(vectorized::Block* block); + + TShowUserResult _user_result; +}; + +} // namespace doris diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/RefreshDbStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/RefreshDbStmt.java index 5251a18b3d6beb..e8bdc168ddcbde 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/RefreshDbStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/RefreshDbStmt.java @@ -19,6 +19,7 @@ import org.apache.doris.catalog.Env; import org.apache.doris.catalog.InfoSchemaDb; +import org.apache.doris.catalog.MysqlDb; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; @@ -79,6 +80,11 @@ public void analyze(Analyzer analyzer) throws AnalysisException, UserException { ErrorReport.reportAnalysisException( ErrorCode.ERR_DBACCESS_DENIED_ERROR, analyzer.getQualifiedUser(), dbName); } + // Don't allow dropping 'mysql' database + if (dbName.equalsIgnoreCase(MysqlDb.DATABASE_NAME)) { + ErrorReport.reportAnalysisException( + ErrorCode.ERR_DBACCESS_DENIED_ERROR, analyzer.getQualifiedUser(), dbName); + } // check access if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), catalogName, dbName, PrivPredicate.DROP)) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaTableType.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaTableType.java index bb1aa5e795295c..2c0901c1e50901 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaTableType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SchemaTableType.java @@ -72,7 +72,9 @@ public enum SchemaTableType { SCH_PROFILING("PROFILING", "PROFILING", TSchemaTableType.SCH_PROFILING), SCH_BACKEND_ACTIVE_TASKS("BACKEND_ACTIVE_TASKS", "BACKEND_ACTIVE_TASKS", TSchemaTableType.SCH_BACKEND_ACTIVE_TASKS), SCH_ACTIVE_QUERIES("ACTIVE_QUERIES", "ACTIVE_QUERIES", TSchemaTableType.SCH_ACTIVE_QUERIES), - SCH_WORKLOAD_GROUPS("WORKLOAD_GROUPS", "WORKLOAD_GROUPS", TSchemaTableType.SCH_WORKLOAD_GROUPS); + SCH_WORKLOAD_GROUPS("WORKLOAD_GROUPS", "WORKLOAD_GROUPS", TSchemaTableType.SCH_WORKLOAD_GROUPS), + SCHE_USER("user", "user", TSchemaTableType.SCH_USER), + SCH_PROCS_PRIV("procs_priv", "procs_priv", TSchemaTableType.SCH_PROCS_PRIV); private static final String dbName = "INFORMATION_SCHEMA"; private static SelectList fullSelectLists; diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDBTable.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDBTable.java new file mode 100644 index 00000000000000..4f3c6cdf57b82c --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDBTable.java @@ -0,0 +1,98 @@ +// 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.catalog; + +import org.apache.doris.analysis.SchemaTableType; +import org.apache.doris.common.SystemIdGenerator; +import org.apache.doris.thrift.TSchemaTable; +import org.apache.doris.thrift.TTableDescriptor; +import org.apache.doris.thrift.TTableType; + +import com.google.common.collect.ImmutableMap; + +import java.util.List; +import java.util.Map; + +/** + * Doris's representation of MySQL mysql table metadata. + */ +public class MysqlDBTable extends SchemaTable { + + public static Map TABLE_MAP = ImmutableMap.builder().put("user", + new MysqlDBTable(SystemIdGenerator.getNextId(), "user", TableType.SCHEMA, + builder().column("Host", ScalarType.createCharType(255)) + .column("User", ScalarType.createCharType(32)) + .column("Node_priv", ScalarType.createCharType(1)) + .column("Admin_priv", ScalarType.createCharType(1)) + .column("Grant_priv", ScalarType.createCharType(1)) + .column("Select_priv", ScalarType.createCharType(1)) + .column("Load_priv", ScalarType.createCharType(1)) + .column("Alter_priv", ScalarType.createCharType(1)) + .column("Create_priv", ScalarType.createCharType(1)) + .column("Drop_priv", ScalarType.createCharType(1)) + .column("Usage_priv", ScalarType.createCharType(1)) + .column("Show_view_priv", ScalarType.createCharType(1)) + .column("Cluster_usage_priv", ScalarType.createCharType(1)) + .column("Stage_usage_priv", ScalarType.createCharType(1)) + .column("ssl_type", ScalarType.createCharType(9)) + .column("ssl_cipher", ScalarType.createVarcharType(ScalarType.MAX_VARCHAR_LENGTH)) + .column("x509_issuer", ScalarType.createVarcharType(ScalarType.MAX_VARCHAR_LENGTH)) + .column("x509_subject", ScalarType.createVarcharType(ScalarType.MAX_VARCHAR_LENGTH)) + .column("max_questions", Type.BIGINT) + .column("max_updates", Type.BIGINT) + .column("max_connections", Type.BIGINT) + .column("max_user_connections", Type.BIGINT) + .column("plugin", ScalarType.createCharType(64)) + .column("authentication_string", + ScalarType.createVarcharType(ScalarType.MAX_VARCHAR_LENGTH)) + .column("password_policy.expiration_seconds", ScalarType.createVarcharType(32)) + .column("password_policy.password_creation_time", ScalarType.createVarcharType(32)) + .column("password_policy.history_num", ScalarType.createVarcharType(32)) + .column("password_policy.history_passwords", + ScalarType.createVarcharType(ScalarType.MAX_VARCHAR_LENGTH)) + .column("password_policy.num_failed_login", ScalarType.createVarcharType(32)) + .column("password_policy.password_lock_seconds", ScalarType.createVarcharType(32)) + .column("password_policy.failed_login_counter", ScalarType.createVarcharType(32)) + .column("password_policy.lock_time", ScalarType.createVarcharType(32)) + .build())) + .put("procs_priv", + new MysqlDBTable(SystemIdGenerator.getNextId(), "procs_priv", TableType.SCHEMA, + builder().column("Host", ScalarType.createCharType(60)) + .column("Db", ScalarType.createCharType(64)) + .column("User", ScalarType.createCharType(32)) + .column("Routine_name", ScalarType.createCharType(64)) + .column("Routine_type", ScalarType.createCharType(9)) + .column("Grantor", ScalarType.createCharType(93)) + .column("Proc_priv", ScalarType.createCharType(16)) + .column("Timestamp", ScalarType.createCharType(1)) + .build())) + .build(); + + public MysqlDBTable(long id, String tableName, TableType type, List fullSchema) { + super(id, tableName, type, fullSchema); + } + + @Override + public TTableDescriptor toThrift() { + TSchemaTable tSchemaTable = new TSchemaTable(SchemaTableType.getThriftType(this.name)); + TTableDescriptor tTableDescriptor = new TTableDescriptor(getId(), TTableType.SCHEMA_TABLE, + TABLE_MAP.get(this.name).getBaseSchema().size(), 0, this.name, ""); + tTableDescriptor.setSchemaTable(tSchemaTable); + return tTableDescriptor; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDb.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDb.java index 67d7fc8ecca078..cec2461b9e4a9b 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDb.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/MysqlDb.java @@ -49,7 +49,11 @@ public MysqlDb() { * If we need tables of mysql database in the future, create a MysqlTable class like {@link SchemaTable} */ @Override - public void initTables() {} + public void initTables() { + for (Table table : MysqlDBTable.TABLE_MAP.values()) { + super.registerTable(table); + } + } @Override public boolean registerTable(TableIf table) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java index a3525321edf192..45d99e02223757 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalCatalog.java @@ -26,6 +26,7 @@ import org.apache.doris.catalog.DatabaseIf; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.InfoSchemaDb; +import org.apache.doris.catalog.MysqlDb; import org.apache.doris.catalog.Resource; import org.apache.doris.catalog.TableIf; import org.apache.doris.cluster.ClusterNamespace; @@ -40,6 +41,7 @@ import org.apache.doris.datasource.hive.HMSExternalDatabase; import org.apache.doris.datasource.iceberg.IcebergExternalDatabase; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaDatabase; +import org.apache.doris.datasource.infoschema.ExternalMysqlDatabase; import org.apache.doris.datasource.jdbc.JdbcExternalDatabase; import org.apache.doris.datasource.maxcompute.MaxComputeExternalDatabase; import org.apache.doris.datasource.operations.ExternalMetadataOps; @@ -291,10 +293,12 @@ protected void init() { allDatabases.remove(InfoSchemaDb.DATABASE_NAME); allDatabases.add(InfoSchemaDb.DATABASE_NAME); + allDatabases.remove(MysqlDb.DATABASE_NAME); + allDatabases.add(MysqlDb.DATABASE_NAME); Map includeDatabaseMap = getIncludeDatabaseMap(); Map excludeDatabaseMap = getExcludeDatabaseMap(); for (String dbName : allDatabases) { - if (!dbName.equals(InfoSchemaDb.DATABASE_NAME)) { + if (!dbName.equals(InfoSchemaDb.DATABASE_NAME) && !dbName.equals(MysqlDb.DATABASE_NAME)) { // Exclude database map take effect with higher priority over include database map if (!excludeDatabaseMap.isEmpty() && excludeDatabaseMap.containsKey(dbName)) { continue; @@ -438,6 +442,9 @@ public ExternalDatabase getDbNullable(String dbName) { if (realDbName.equalsIgnoreCase(InfoSchemaDb.DATABASE_NAME)) { return idToDb.get(dbNameToId.get(InfoSchemaDb.DATABASE_NAME)); } + if (realDbName.equalsIgnoreCase(MysqlDb.DATABASE_NAME)) { + return idToDb.get(dbNameToId.get(MysqlDb.DATABASE_NAME)); + } } return null; } @@ -549,6 +556,9 @@ protected ExternalDatabase getDbForInit(String dbName, if (dbName.equals(InfoSchemaDb.DATABASE_NAME)) { return new ExternalInfoSchemaDatabase(this, dbId); } + if (dbName.equals(MysqlDb.DATABASE_NAME)) { + return new ExternalMysqlDatabase(this, dbId); + } switch (logType) { case HMS: return new HMSExternalDatabase(this, dbId, dbName); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java index 2d0d47f633c4be..3308b04968f75d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalDatabase.java @@ -21,6 +21,7 @@ import org.apache.doris.catalog.DatabaseProperty; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.InfoSchemaDb; +import org.apache.doris.catalog.MysqlDb; import org.apache.doris.catalog.TableIf; import org.apache.doris.common.DdlException; import org.apache.doris.common.MetaNotFoundException; @@ -29,6 +30,8 @@ import org.apache.doris.common.util.Util; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaDatabase; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaTable; +import org.apache.doris.datasource.infoschema.ExternalMysqlDatabase; +import org.apache.doris.datasource.infoschema.ExternalMysqlTable; import org.apache.doris.persist.gson.GsonPostProcessable; import org.apache.doris.persist.gson.GsonUtils; import org.apache.doris.qe.ConnectContext; @@ -60,7 +63,7 @@ * @param External table type is ExternalTable or its subclass. */ public abstract class ExternalDatabase - implements DatabaseIf, Writable, GsonPostProcessable { + implements DatabaseIf, Writable, GsonPostProcessable { private static final Logger LOG = LogManager.getLogger(ExternalDatabase.class); protected ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true); @@ -173,6 +176,8 @@ protected void init() { List tableNames; if (name.equals(InfoSchemaDb.DATABASE_NAME)) { tableNames = ExternalInfoSchemaDatabase.listTableNames(); + } else if (name.equals(MysqlDb.DATABASE_NAME)) { + tableNames = ExternalMysqlDatabase.listTableNames(); } else { tableNames = extCatalog.listTableNames(null, name); } @@ -350,10 +355,23 @@ public void gsonPostProcess() throws IOException { Map tmpIdToTbl = Maps.newConcurrentMap(); for (Object obj : idToTbl.values()) { if (obj instanceof LinkedTreeMap) { - ExternalInfoSchemaTable table = GsonUtils.GSON.fromJson(GsonUtils.GSON.toJson(obj), - ExternalInfoSchemaTable.class); - tmpIdToTbl.put(table.getId(), (T) table); - tableNameToId.put(table.getName(), table.getId()); + String clazzType = ((LinkedTreeMap) obj).get("clazz").toString(); + switch (clazzType) { + case "ExternalInfoSchemaTable": + ExternalInfoSchemaTable infoSchemaTable = GsonUtils.GSON.fromJson(GsonUtils.GSON.toJson(obj), + ExternalInfoSchemaTable.class); + tmpIdToTbl.put(infoSchemaTable.getId(), (T) infoSchemaTable); + tableNameToId.put(infoSchemaTable.getName(), infoSchemaTable.getId()); + break; + case "ExternalMysqlTable": + ExternalMysqlTable mysqlTable = GsonUtils.GSON.fromJson(GsonUtils.GSON.toJson(obj), + ExternalMysqlTable.class); + tmpIdToTbl.put(mysqlTable.getId(), (T) mysqlTable); + tableNameToId.put(mysqlTable.getName(), mysqlTable.getId()); + break; + default: + break; + } } else { Preconditions.checkState(obj instanceof ExternalTable); tmpIdToTbl.put(((ExternalTable) obj).getId(), (T) obj); diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlDatabase.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlDatabase.java new file mode 100644 index 00000000000000..8e9ba26ab2bba4 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlDatabase.java @@ -0,0 +1,55 @@ +// 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.infoschema; + +import org.apache.doris.catalog.MysqlDBTable; +import org.apache.doris.catalog.MysqlDb; +import org.apache.doris.datasource.ExternalCatalog; +import org.apache.doris.datasource.ExternalDatabase; +import org.apache.doris.datasource.ExternalTable; +import org.apache.doris.datasource.InitDatabaseLog.Type; + +import com.google.common.collect.Lists; + +import java.util.List; + +public class ExternalMysqlDatabase extends ExternalDatabase { + /** + * Create external database. + * + * @param extCatalog The catalog this database belongs to. + * @param dbId The id of this database. + */ + public ExternalMysqlDatabase(ExternalCatalog extCatalog, long dbId) { + super(extCatalog, dbId, MysqlDb.DATABASE_NAME, Type.INFO_SCHEMA_DB); + } + + public static List listTableNames() { + return Lists.newArrayList(MysqlDBTable.TABLE_MAP.keySet()); + } + + @Override + protected ExternalTable newExternalTable(String tableName, long tblId, ExternalCatalog catalog) { + return new ExternalMysqlTable(tblId, tableName, catalog); + } + + @Override + public ExternalTable getTableNullable(String name) { + return super.getTableNullable(name.toLowerCase()); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlTable.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlTable.java new file mode 100644 index 00000000000000..fc64bc053a479e --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/infoschema/ExternalMysqlTable.java @@ -0,0 +1,51 @@ +// 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.infoschema; + +import org.apache.doris.analysis.SchemaTableType; +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.MysqlDBTable; +import org.apache.doris.catalog.MysqlDb; +import org.apache.doris.datasource.ExternalCatalog; +import org.apache.doris.datasource.ExternalTable; +import org.apache.doris.thrift.TSchemaTable; +import org.apache.doris.thrift.TTableDescriptor; +import org.apache.doris.thrift.TTableType; + +import java.util.List; + +public class ExternalMysqlTable extends ExternalTable { + public ExternalMysqlTable(long id, String name, ExternalCatalog catalog) { + super(id, name, catalog, MysqlDb.DATABASE_NAME, TableType.SCHEMA); + } + + @Override + public List initSchema() { + makeSureInitialized(); + return MysqlDBTable.TABLE_MAP.get(name).getFullSchema(); + } + + @Override + public TTableDescriptor toThrift() { + TSchemaTable tSchemaTable = new TSchemaTable(SchemaTableType.getThriftType(this.name)); + TTableDescriptor tTableDescriptor = new TTableDescriptor(getId(), TTableType.SCHEMA_TABLE, + getFullSchema().size(), 0, this.name, ""); + tTableDescriptor.setSchemaTable(tSchemaTable); + return tTableDescriptor; + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java index a95c6c74f2b269..7e093f1008c5cd 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java @@ -409,6 +409,8 @@ protected List filterDatabaseNames(List remoteDbNames) { protected Set getFilterInternalDatabases() { return ImmutableSet.builder() .add("information_schema") + .add("performance_schema") + .add("mysql") .build(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java index 20047b29be2110..6ea5ea127d56c4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java @@ -173,6 +173,8 @@ protected String getCatalogName(Connection conn) throws SQLException { protected Set getFilterInternalDatabases() { return ImmutableSet.builder() + .add("information_schema") + .add("performance_schema") .add("mysql") .add("sys") .build(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java index 182863e7ac96c0..3bad8c318ccf62 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Auth.java @@ -77,6 +77,7 @@ import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -1812,4 +1813,97 @@ public String toString() { sb.append(ldapInfo).append("\n"); return sb.toString(); } + + // for mysql.user table + public List> getAllUserInfo() { + List> userInfos = Lists.newArrayList(); + readLock(); + try { + Map> nameToUsers = userManager.getNameToUsers(); + for (List users : nameToUsers.values()) { + for (User user : users) { + if (!user.isSetByDomainResolver()) { + List userInfo = Lists.newArrayList(Collections.nCopies(32, "")); + UserIdentity userIdent = user.getUserIdentity(); + userInfo.set(0, userIdent.getHost()); + userInfo.set(1, userIdent.getQualifiedUser()); + for (int i = 2; i <= 13; i++) { + userInfo.set(i, "N"); + } + + PrivTable privTable = getUserGlobalPrivTable(userIdent); + if (!privTable.entries.isEmpty()) { + PrivEntry privEntry = privTable.entries.get(0); + if (!privEntry.getPrivSet().isEmpty()) { + boolean isAdmin = false; + for (Privilege globalPriv : privEntry.getPrivSet().toPrivilegeList()) { + switch (globalPriv) { + case NODE_PRIV: + userInfo.set(2, "Y"); + break; + case ADMIN_PRIV: + isAdmin = true; + userInfo.set(3, "Y"); + break; + case GRANT_PRIV: + userInfo.set(4, "Y"); + break; + case SELECT_PRIV: + userInfo.set(5, "Y"); + break; + case LOAD_PRIV: + userInfo.set(6, "Y"); + break; + case ALTER_PRIV: + userInfo.set(7, "Y"); + break; + case CREATE_PRIV: + userInfo.set(8, "Y"); + break; + case DROP_PRIV: + userInfo.set(9, "Y"); + break; + case USAGE_PRIV: + userInfo.set(10, "Y"); + break; + case SHOW_VIEW_PRIV: + userInfo.set(11, "Y"); + break; + case CLUSTER_USAGE_PRIV: + userInfo.set(12, "Y"); + break; + case STAGE_USAGE_PRIV: + userInfo.set(13, "Y"); + break; + default: + break; + } + } + + // If the user is admin, set all permissions to 'Y' except Node_priv + if (isAdmin) { + for (int i = 4; i <= 13; i++) { + userInfo.set(i, "Y"); + } + } + } + } + + // Set password policy info + userInfo.set(21, String.valueOf(getMaxConn(userIdent.getQualifiedUser()))); + List> passWordPolicyInfo = getPasswdPolicyInfo(userIdent); + if (passWordPolicyInfo.size() == 8) { + for (int i = 0; i < passWordPolicyInfo.size(); i++) { + userInfo.set(24 + i, passWordPolicyInfo.get(i).get(1)); + } + } + userInfos.add(userInfo); + } + } + } + } finally { + readUnlock(); + } + return userInfos; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/persist/gson/GsonUtils.java b/fe/fe-core/src/main/java/org/apache/doris/persist/gson/GsonUtils.java index 6d71cd58826a53..d380d6824fa411 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/persist/gson/GsonUtils.java +++ b/fe/fe-core/src/main/java/org/apache/doris/persist/gson/GsonUtils.java @@ -69,6 +69,8 @@ import org.apache.doris.datasource.iceberg.IcebergRestExternalCatalog; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaDatabase; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaTable; +import org.apache.doris.datasource.infoschema.ExternalMysqlDatabase; +import org.apache.doris.datasource.infoschema.ExternalMysqlTable; import org.apache.doris.datasource.jdbc.JdbcExternalCatalog; import org.apache.doris.datasource.jdbc.JdbcExternalDatabase; import org.apache.doris.datasource.jdbc.JdbcExternalTable; @@ -261,6 +263,7 @@ public class GsonUtils { .registerSubtype(PaimonExternalDatabase.class, PaimonExternalDatabase.class.getSimpleName()) .registerSubtype(MaxComputeExternalDatabase.class, MaxComputeExternalDatabase.class.getSimpleName()) .registerSubtype(ExternalInfoSchemaDatabase.class, ExternalInfoSchemaDatabase.class.getSimpleName()) + .registerSubtype(ExternalMysqlDatabase.class, ExternalMysqlDatabase.class.getSimpleName()) .registerSubtype(TestExternalDatabase.class, TestExternalDatabase.class.getSimpleName()); private static RuntimeTypeAdapterFactory tblTypeAdapterFactory = RuntimeTypeAdapterFactory.of( @@ -273,6 +276,7 @@ public class GsonUtils { .registerSubtype(PaimonExternalTable.class, PaimonExternalTable.class.getSimpleName()) .registerSubtype(MaxComputeExternalTable.class, MaxComputeExternalTable.class.getSimpleName()) .registerSubtype(ExternalInfoSchemaTable.class, ExternalInfoSchemaTable.class.getSimpleName()) + .registerSubtype(ExternalMysqlTable.class, ExternalMysqlTable.class.getSimpleName()) .registerSubtype(TestExternalTable.class, TestExternalTable.class.getSimpleName()); // runtime adapter for class "PartitionInfo" diff --git a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java index 87adc37722c99a..5269a43898cb1f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java +++ b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java @@ -212,6 +212,8 @@ import org.apache.doris.thrift.TSchemaTableName; import org.apache.doris.thrift.TShowProcessListRequest; import org.apache.doris.thrift.TShowProcessListResult; +import org.apache.doris.thrift.TShowUserRequest; +import org.apache.doris.thrift.TShowUserResult; import org.apache.doris.thrift.TShowVariableRequest; import org.apache.doris.thrift.TShowVariableResult; import org.apache.doris.thrift.TSnapshotLoaderReportRequest; @@ -3843,4 +3845,11 @@ public TShowProcessListResult showProcessList(TShowProcessListRequest request) { return result; } + @Override + public TShowUserResult showUser(TShowUserRequest request) { + List> userInfo = Env.getCurrentEnv().getAuth().getAllUserInfo(); + TShowUserResult result = new TShowUserResult(); + result.setUserinfoList(userInfo); + return result; + } } diff --git a/fe/fe-core/src/test/java/org/apache/doris/catalog/RefreshTableTest.java b/fe/fe-core/src/test/java/org/apache/doris/catalog/RefreshTableTest.java index c979926335ea17..d37058c20cd67d 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/catalog/RefreshTableTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/catalog/RefreshTableTest.java @@ -30,6 +30,8 @@ import org.apache.doris.datasource.CatalogIf; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaDatabase; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaTable; +import org.apache.doris.datasource.infoschema.ExternalMysqlDatabase; +import org.apache.doris.datasource.infoschema.ExternalMysqlTable; import org.apache.doris.datasource.test.TestExternalCatalog; import org.apache.doris.datasource.test.TestExternalTable; import org.apache.doris.mysql.privilege.Auth; @@ -109,6 +111,15 @@ public void testRefreshTable() throws Exception { List schema = infoTbl.getFullSchema(); Assertions.assertEquals(SchemaTable.TABLE_MAP.get(tblName).getColumns().size(), schema.size()); } + // external mysql db + ExternalMysqlDatabase mysqlDb = (ExternalMysqlDatabase) test1.getDbNullable(MysqlDb.DATABASE_NAME); + Assertions.assertNotNull(mysqlDb); + for (String tblName : MysqlDBTable.TABLE_MAP.keySet()) { + ExternalMysqlTable mysqlTbl = (ExternalMysqlTable) mysqlDb.getTableNullable(tblName); + Assertions.assertNotNull(mysqlTbl); + List schema = mysqlTbl.getFullSchema(); + Assertions.assertEquals(MysqlDBTable.TABLE_MAP.get(tblName).getColumns().size(), schema.size()); + } } @Test diff --git a/fe/fe-core/src/test/java/org/apache/doris/datasource/RefreshCatalogTest.java b/fe/fe-core/src/test/java/org/apache/doris/datasource/RefreshCatalogTest.java index 8480154f243e51..0cdd0db8ae3e26 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/datasource/RefreshCatalogTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/datasource/RefreshCatalogTest.java @@ -22,9 +22,11 @@ import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.InfoSchemaDb; +import org.apache.doris.catalog.MysqlDb; import org.apache.doris.catalog.PrimitiveType; import org.apache.doris.common.FeConstants; import org.apache.doris.datasource.infoschema.ExternalInfoSchemaDatabase; +import org.apache.doris.datasource.infoschema.ExternalMysqlDatabase; import org.apache.doris.datasource.test.TestExternalCatalog; import org.apache.doris.datasource.test.TestExternalDatabase; import org.apache.doris.mysql.privilege.Auth; @@ -77,19 +79,21 @@ protected void runAfterAll() throws Exception { public void testRefreshCatalog() throws Exception { CatalogIf test1 = env.getCatalogMgr().getCatalog("test1"); List dbNames1 = test1.getDbNames(); - // there are test1.db1 , test1.db2, information_schema - Assertions.assertEquals(3, dbNames1.size()); + // there are test1.db1 , test1.db2, information_schema, mysql + Assertions.assertEquals(4, dbNames1.size()); // 1.simulate ExternalCatalog adds a new table RefreshCatalogProvider.addData(); // 2.wait for the refresh time of the catalog Thread.sleep(5000); - // there are test1.db1 , test1.db2 , test1.db3, information_schema + // there are test1.db1 , test1.db2 , test1.db3, information_schema, mysql List dbNames2 = test1.getDbNames(); - Assertions.assertEquals(4, dbNames2.size()); + Assertions.assertEquals(5, dbNames2.size()); ExternalInfoSchemaDatabase infoDb = (ExternalInfoSchemaDatabase) test1.getDb(InfoSchemaDb.DATABASE_NAME).get(); Assertions.assertEquals(31, infoDb.getTables().size()); TestExternalDatabase testDb = (TestExternalDatabase) test1.getDb("db1").get(); Assertions.assertEquals(2, testDb.getTables().size()); + ExternalMysqlDatabase mysqlDb = (ExternalMysqlDatabase) test1.getDb(MysqlDb.DATABASE_NAME).get(); + Assertions.assertEquals(2, mysqlDb.getTables().size()); String json = GsonUtils.GSON.toJson(env.getCatalogMgr()); System.out.println(json); @@ -99,6 +103,8 @@ public void testRefreshCatalog() throws Exception { Assertions.assertEquals(31, infoDb.getTables().size()); testDb = (TestExternalDatabase) test1.getDb("db1").get(); Assertions.assertEquals(2, testDb.getTables().size()); + mysqlDb = (ExternalMysqlDatabase) test1.getDb(MysqlDb.DATABASE_NAME).get(); + Assertions.assertEquals(2, mysqlDb.getTables().size()); } public static class RefreshCatalogProvider implements TestExternalCatalog.TestCatalogProvider { diff --git a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java index ed7cc927273e1a..2e5a4e0745631e 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java @@ -39,6 +39,8 @@ import org.apache.doris.thrift.TMetadataType; import org.apache.doris.thrift.TNullableStringLiteral; import org.apache.doris.thrift.TSchemaTableName; +import org.apache.doris.thrift.TShowUserRequest; +import org.apache.doris.thrift.TShowUserResult; import org.apache.doris.thrift.TStatusCode; import org.apache.doris.utframe.UtFrameUtils; @@ -220,4 +222,12 @@ public void fetchSchemaTableData() throws Exception { Assert.assertEquals(result.getStatus().getStatusCode(), TStatusCode.OK); Assert.assertEquals(result.getDataBatchSize(), 1); } + + @Test + public void testShowUser() { + FrontendServiceImpl impl = new FrontendServiceImpl(exeEnv); + TShowUserRequest request = new TShowUserRequest(); + TShowUserResult result = impl.showUser(request); + System.out.println(result); + } } diff --git a/gensrc/thrift/Descriptors.thrift b/gensrc/thrift/Descriptors.thrift index d8bbe0e244ab9f..d99ea0bea86b94 100644 --- a/gensrc/thrift/Descriptors.thrift +++ b/gensrc/thrift/Descriptors.thrift @@ -128,7 +128,9 @@ enum TSchemaTableType { SCH_PROFILING, SCH_BACKEND_ACTIVE_TASKS, SCH_ACTIVE_QUERIES, - SCH_WORKLOAD_GROUPS; + SCH_WORKLOAD_GROUPS, + SCH_USER, + SCH_PROCS_PRIV; } enum THdfsCompression { diff --git a/gensrc/thrift/FrontendService.thrift b/gensrc/thrift/FrontendService.thrift index 150783dcae3488..1bd12c364bba14 100644 --- a/gensrc/thrift/FrontendService.thrift +++ b/gensrc/thrift/FrontendService.thrift @@ -1411,6 +1411,13 @@ struct TShowProcessListResult { 1: optional list> process_list } +struct TShowUserRequest { +} + +struct TShowUserResult { + 1: optional list> userinfo_list +} + service FrontendService { TGetDbsResult getDbNames(1: TGetDbsParams params) TGetTablesResult getTableNames(1: TGetTablesParams params) @@ -1499,4 +1506,5 @@ service FrontendService { Status.TStatus invalidateStatsCache(1: TInvalidateFollowerStatsCacheRequest request) TShowProcessListResult showProcessList(1: TShowProcessListRequest request) + TShowUserResult showUser(1: TShowUserRequest request) } diff --git a/regression-test/data/external_table_p0/jdbc/test_clickhouse_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_clickhouse_jdbc_catalog.out index 38aea106c1431c983f4252bd5bbf73a7beb18266..f5123a6ee863cf264426ce3f2b259141d8a109ab 100644 GIT binary patch delta 16 YcmaE_^;K)a6xQ6z;=-JblkbQC07MxF%m4rY delta 10 ScmeyW^ Gambardella MatthewXML Developers GuideComputer44.952000-10-01An in-depth look at creating application\n with XML diff --git a/regression-test/data/external_table_p0/jdbc/test_mariadb_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_mariadb_jdbc_catalog.out index cea4cfe4aef802..01ba13f742a884 100644 --- a/regression-test/data/external_table_p0/jdbc/test_mariadb_jdbc_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_mariadb_jdbc_catalog.out @@ -5,7 +5,7 @@ internal -- !show_db -- doris_test information_schema -performance_schema +mysql -- !sql -- internal diff --git a/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_catalog.out index f3a9f5f937c6de..4586d38228a3c6 100644 --- a/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_catalog.out @@ -274,10 +274,12 @@ doris3 20 -- !specified_database_1 -- doris_test information_schema +mysql -- !specified_database_2 -- doris_test information_schema +mysql -- !specified_database_3 -- DORIS @@ -285,11 +287,12 @@ Doris doris information_schema init_db -performance_schema +mysql show_test_do_not_modify -- !specified_database_4 -- information_schema +mysql -- !ex_tb1 -- {"k1":"v1", "k2":"v2"} @@ -427,7 +430,7 @@ doris_3 doris_test information_schema init_db -performance_schema +mysql show_test_do_not_modify -- !sql -- diff --git a/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_driver5_catalog.out b/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_driver5_catalog.out index b3c63311cc3765..35c805ab89699a 100644 --- a/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_driver5_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_mysql_jdbc_driver5_catalog.out @@ -9,7 +9,7 @@ doris doris_test information_schema init_db -performance_schema +mysql show_test_do_not_modify -- !sql -- @@ -284,10 +284,12 @@ doris3 20 -- !specified_database_1 -- doris_test information_schema +mysql -- !specified_database_2 -- doris_test information_schema +mysql -- !specified_database_3 -- DORIS @@ -295,11 +297,12 @@ Doris doris information_schema init_db -performance_schema +mysql show_test_do_not_modify -- !specified_database_4 -- information_schema +mysql -- !ex_tb1 -- {"k1":"v1", "k2":"v2"} @@ -437,7 +440,7 @@ doris_3 doris_test information_schema init_db -performance_schema +mysql show_test_do_not_modify -- !sql -- diff --git a/regression-test/data/external_table_p0/jdbc/test_oracle_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_oracle_jdbc_catalog.out index 21613b426abfe8..c5473a451e8284 100644 --- a/regression-test/data/external_table_p0/jdbc/test_oracle_jdbc_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_oracle_jdbc_catalog.out @@ -6,6 +6,7 @@ APEX_PUBLIC_USER DORIS_TEST HR information_schema +mysql -- !test0 -- 1 alice 20 99.5 @@ -223,10 +224,12 @@ TINYINT_VALUE2 SMALLINT Yes false \N NONE -- !specified_database -- DORIS_TEST information_schema +mysql -- !specified_database -- DORIS_TEST information_schema +mysql -- !lower_case_table_names1 -- 1 111 123 7456123.89 573 34 673.43 34.1264 60.0 23.231 diff --git a/regression-test/data/external_table_p0/jdbc/test_pg_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_pg_jdbc_catalog.out index 728f48a2999a6a..0c0598f4661787 100644 --- a/regression-test/data/external_table_p0/jdbc/test_pg_jdbc_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_pg_jdbc_catalog.out @@ -3,6 +3,7 @@ catalog_pg_test doris_test information_schema +mysql pg_catalog public @@ -2274,19 +2275,23 @@ varchar_value TEXT Yes false \N NONE -- !specified_database_1 -- doris_test information_schema +mysql -- !specified_database_2 -- doris_test information_schema +mysql -- !specified_database_3 -- catalog_pg_test information_schema +mysql pg_catalog public -- !specified_database_4 -- information_schema +mysql -- !test_old -- 123 abc diff --git a/regression-test/data/external_table_p0/jdbc/test_sqlserver_jdbc_catalog.out b/regression-test/data/external_table_p0/jdbc/test_sqlserver_jdbc_catalog.out index d0d394cdee2f0e..f646561b0c964e 100644 --- a/regression-test/data/external_table_p0/jdbc/test_sqlserver_jdbc_catalog.out +++ b/regression-test/data/external_table_p0/jdbc/test_sqlserver_jdbc_catalog.out @@ -12,6 +12,7 @@ db_securityadmin dbo guest information_schema +mysql sys -- !test0 -- @@ -142,5 +143,6 @@ db_securityadmin dbo guest information_schema +mysql sys