This is an automated email from the ASF dual-hosted git repository.

dataroaring pushed a commit to branch branch-3.0
in repository https://gitbox.apache.org/repos/asf/doris.git

commit fa422e5188ed0924f6bfecded741b3d1a35c0e24
Author: Xujian Duan <[email protected]>
AuthorDate: Fri Aug 16 20:52:23 2024 +0800

    [Feature](show) show backend config using ShowStmt (#36525)
    
    Currently, Doris only supports accessing backend configurations via HTTP
    APIs. To enhance this functionality, I have added some syntax extensions
    to allow displaying backend configurations using SQL commands.
    
    The SQL syntax for displaying backend configurations is similar to that
    for displaying frontend configurations:
    
    1. Display backend configurations matching a pattern:
    This statement will display all active backend configurations that match
    the specified pattern.
    
    show backend config like ${pattern}
    
    3. Display backend configurations for a specific BE using the backend ID
    
    show backend config from ${beId}
    
    4. Use both a pattern and a backend ID:
    
    show backend config like ${pattern} from ${backendID}
    
    
    The output of these statements consists of 6 columns: BE ID, BE host IP,
    configuration key, configuration value, configuration type, and whether
    it is mutable or not.
    
    +-----------+---------------+---------+-------+---------+-----------+
    | BackendId | Host          | Key     | Value | Type    | IsMutable |
    +-----------+---------------+---------+-------+---------+-----------+
    | 10001     | xx.xx.xxx.xxx | be_port | 9060  | int32_t | false     |
    +-----------+---------------+---------+-------+---------+-----------+
    
    This provides an additional way to access backend configurations within
    Doris.
    
    
    Co-authored-by: duanxujian <[email protected]>
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |   1 +
 .../antlr4/org/apache/doris/nereids/PLParser.g4    |   5 +
 fe/fe-core/src/main/cup/sql_parser.cup             |  27 +++-
 .../apache/doris/analysis/AdminSetConfigStmt.java  |  14 +-
 .../org/apache/doris/analysis/ShowConfigStmt.java  |  55 +++++--
 .../doris/nereids/parser/LogicalPlanBuilder.java   |  23 +++
 .../apache/doris/nereids/trees/plans/PlanType.java |   3 +-
 .../trees/plans/commands/ShowConfigCommand.java    | 171 +++++++++++++++++++++
 .../trees/plans/visitor/CommandVisitor.java        |   5 +
 .../java/org/apache/doris/qe/ShowExecutor.java     |  79 +++++++++-
 .../java/org/apache/doris/system/NodeType.java     |  23 +++
 .../suites/show_p0/test_show_backend_config.groovy |  57 +++++++
 12 files changed, 427 insertions(+), 36 deletions(-)

diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
index ab316cf2794..c86201a2935 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4
@@ -40,6 +40,7 @@ statement
     | DROP (PROCEDURE | PROC) (IF EXISTS)? name=multipartIdentifier 
#dropProcedure
     | SHOW PROCEDURE STATUS (LIKE pattern=valueExpression | whereClause)? 
#showProcedureStatus
     | SHOW CREATE PROCEDURE name=multipartIdentifier #showCreateProcedure
+    | ADMIN? SHOW type=(FRONTEND | BACKEND) CONFIG (LIKE 
pattern=valueExpression)? (FROM backendId=INTEGER_VALUE)? #showConfig
     ;
 
 statementBase
diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/PLParser.g4 
b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/PLParser.g4
index e967da2f955..7051cac0ca0 100644
--- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/PLParser.g4
+++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/PLParser.g4
@@ -101,6 +101,7 @@ stmt :
      | drop_procedure_stmt
      | show_procedure_stmt
      | show_create_procedure_stmt
+     | show_config_stmt
      | exec_stmt
      | exit_stmt
      | fetch_stmt
@@ -343,6 +344,10 @@ show_create_procedure_stmt:
       SHOW CREATE PROCEDURE name=multipartIdentifier
     ;      
 
+show_config_stmt:
+      SHOW type=(FRONTEND | BACKEND) CONFIG (LIKE pattern=valueExpression)? 
(FROM backendId=INTEGER_VALUE)?
+    ;
+
 create_routine_params :
        LEFT_PAREN RIGHT_PAREN
      | LEFT_PAREN create_routine_param_item (COMMA create_routine_param_item)* 
RIGHT_PAREN
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index f19b9f4f531..37907e44915 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -60,6 +60,7 @@ import org.apache.doris.load.loadv2.LoadTask;
 import org.apache.doris.policy.PolicyTypeEnum;
 import org.apache.doris.resource.workloadschedpolicy.WorkloadConditionMeta;
 import org.apache.doris.resource.workloadschedpolicy.WorkloadActionMeta;
+import org.apache.doris.system.NodeType;
 
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -4705,7 +4706,15 @@ show_param ::=
     :}
     | KW_FRONTEND KW_CONFIG opt_wild_where
     {:
-        RESULT = new ShowConfigStmt(AdminSetConfigStmt.ConfigType.FRONTEND, 
parser.wild);
+        RESULT = new ShowConfigStmt(NodeType.FRONTEND, parser.wild);
+    :}
+    | KW_BACKEND KW_CONFIG opt_wild_where
+    {:
+        RESULT = new ShowConfigStmt(NodeType.BACKEND, parser.wild);
+    :}
+    | KW_BACKEND KW_CONFIG opt_wild_where KW_FROM INTEGER_LITERAL:backendId
+    {:
+        RESULT = new ShowConfigStmt(NodeType.BACKEND, parser.wild, backendId);
     :}
     | KW_TABLET KW_STORAGE KW_FORMAT
     {:
@@ -7828,20 +7837,28 @@ admin_stmt ::=
     :}
     | KW_ADMIN KW_SET KW_FRONTEND KW_CONFIG opt_key_value_map:configs
     {:
-        RESULT = new 
AdminSetConfigStmt(AdminSetConfigStmt.ConfigType.FRONTEND, configs, false);
+        RESULT = new AdminSetConfigStmt(NodeType.FRONTEND, configs, false);
     :}
     | KW_ADMIN KW_SET KW_ALL KW_FRONTENDS KW_CONFIG opt_key_value_map:configs
     {:
-        RESULT = new 
AdminSetConfigStmt(AdminSetConfigStmt.ConfigType.FRONTEND, configs, true);
+        RESULT = new AdminSetConfigStmt(NodeType.FRONTEND, configs, true);
     :}
     | KW_ADMIN KW_SET KW_FRONTEND KW_CONFIG opt_key_value_map:configs KW_ALL
     {:
-        RESULT = new 
AdminSetConfigStmt(AdminSetConfigStmt.ConfigType.FRONTEND, configs, true);
+        RESULT = new AdminSetConfigStmt(NodeType.FRONTEND, configs, true);
     :}
     // deprecated
     | KW_ADMIN KW_SHOW KW_FRONTEND KW_CONFIG opt_wild_where
     {:
-        RESULT = new ShowConfigStmt(AdminSetConfigStmt.ConfigType.FRONTEND, 
parser.wild);
+        RESULT = new ShowConfigStmt(NodeType.FRONTEND, parser.wild);
+    :}
+    | KW_ADMIN KW_SHOW KW_BACKEND KW_CONFIG opt_wild_where
+    {:
+        RESULT = new ShowConfigStmt(NodeType.BACKEND, parser.wild);
+    :}
+    | KW_ADMIN KW_SHOW KW_BACKEND KW_CONFIG opt_wild_where KW_FROM 
INTEGER_LITERAL:backendId
+    {:
+        RESULT = new ShowConfigStmt(NodeType.BACKEND, parser.wild, backendId);
     :}
     | KW_ADMIN KW_CHECK KW_TABLET LPAREN integer_list:tabletIds RPAREN 
opt_properties:properties
     {:
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminSetConfigStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminSetConfigStmt.java
index 1d2e22ee878..0de5ee0807d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminSetConfigStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AdminSetConfigStmt.java
@@ -26,6 +26,7 @@ import org.apache.doris.common.UserException;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.OriginStatement;
+import org.apache.doris.system.NodeType;
 
 import com.google.common.collect.Maps;
 
@@ -34,18 +35,13 @@ import java.util.Map;
 // admin set frontend config ("key" = "value");
 public class AdminSetConfigStmt extends DdlStmt {
 
-    public enum ConfigType {
-        FRONTEND,
-        BACKEND
-    }
-
     private boolean applyToAll;
-    private ConfigType type;
+    private NodeType type;
     private Map<String, String> configs;
 
     private RedirectStatus redirectStatus = RedirectStatus.NO_FORWARD;
 
-    public AdminSetConfigStmt(ConfigType type, Map<String, String> configs, 
boolean applyToAll) {
+    public AdminSetConfigStmt(NodeType type, Map<String, String> configs, 
boolean applyToAll) {
         this.type = type;
         this.configs = configs;
         if (this.configs == null) {
@@ -62,7 +58,7 @@ public class AdminSetConfigStmt extends DdlStmt {
         }
     }
 
-    public ConfigType getType() {
+    public NodeType getType() {
         return type;
     }
 
@@ -86,7 +82,7 @@ public class AdminSetConfigStmt extends DdlStmt {
             
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
         }
 
-        if (type != ConfigType.FRONTEND) {
+        if (type != NodeType.FRONTEND) {
             throw new AnalysisException("Only support setting Frontend configs 
now");
         }
     }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowConfigStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowConfigStmt.java
index 62b91a32420..b32c91d4a66 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowConfigStmt.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowConfigStmt.java
@@ -17,35 +17,48 @@
 
 package org.apache.doris.analysis;
 
-import org.apache.doris.analysis.AdminSetConfigStmt.ConfigType;
 import org.apache.doris.catalog.Column;
 import org.apache.doris.catalog.Env;
 import org.apache.doris.catalog.ScalarType;
-import org.apache.doris.common.AnalysisException;
 import org.apache.doris.common.ErrorCode;
 import org.apache.doris.common.ErrorReport;
 import org.apache.doris.common.UserException;
 import org.apache.doris.mysql.privilege.PrivPredicate;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.ShowResultSetMetaData;
+import org.apache.doris.system.NodeType;
 
 import com.google.common.collect.ImmutableList;
 
 // show frontend config;
-public class ShowConfigStmt extends ShowStmt {
-    public static final ImmutableList<String> TITLE_NAMES = new 
ImmutableList.Builder<String>().add("Key").add(
+public class ShowConfigStmt extends ShowStmt implements NotFallbackInParser {
+    public static final ImmutableList<String> FE_TITLE_NAMES = new 
ImmutableList.Builder<String>().add("Key").add(
             
"Value").add("Type").add("IsMutable").add("MasterOnly").add("Comment").build();
 
-    private ConfigType type;
+    public static final ImmutableList<String> BE_TITLE_NAMES = new 
ImmutableList.Builder<String>().add("BackendId")
+            
.add("Host").add("Key").add("Value").add("Type").add("IsMutable").build();
+
+    private NodeType type;
 
     private String pattern;
 
-    public ShowConfigStmt(ConfigType type, String pattern) {
+    private long backendId;
+
+    private boolean isShowSingleBackend;
+
+    public ShowConfigStmt(NodeType type, String pattern) {
+        this.type = type;
+        this.pattern = pattern;
+    }
+
+    public ShowConfigStmt(NodeType type, String pattern, long backendId) {
         this.type = type;
         this.pattern = pattern;
+        this.backendId = backendId;
+        this.isShowSingleBackend = true;
     }
 
-    public ConfigType getType() {
+    public NodeType getType() {
         return type;
     }
 
@@ -53,31 +66,45 @@ public class ShowConfigStmt extends ShowStmt {
         return pattern;
     }
 
+    public long getBackendId() {
+        return backendId;
+    }
+
+    public boolean isShowSingleBackend() {
+        return isShowSingleBackend;
+    }
+
     @Override
-    public void analyze(Analyzer analyzer) throws AnalysisException, 
UserException {
+    public void analyze(Analyzer analyzer) throws UserException {
         super.analyze(analyzer);
 
         // check auth
         if 
(!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), 
PrivPredicate.ADMIN)) {
             
ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, 
"ADMIN");
         }
-
-        if (type != ConfigType.FRONTEND) {
-            throw new AnalysisException("Only support setting Frontend configs 
now");
-        }
     }
 
     @Override
     public ShowResultSetMetaData getMetaData() {
         ShowResultSetMetaData.Builder builder = 
ShowResultSetMetaData.builder();
-        for (String title : TITLE_NAMES) {
-            builder.addColumn(new Column(title, ScalarType.createVarchar(30)));
+        if (type == NodeType.FRONTEND) {
+            for (String title : FE_TITLE_NAMES) {
+                builder.addColumn(new Column(title, 
ScalarType.createVarchar(30)));
+            }
+        } else {
+            for (String title : BE_TITLE_NAMES) {
+                builder.addColumn(new Column(title, 
ScalarType.createVarchar(30)));
+            }
         }
         return builder.build();
     }
 
     @Override
     public RedirectStatus getRedirectStatus() {
+        // no need forward to master for backend config
+        if (type == NodeType.BACKEND) {
+            return RedirectStatus.NO_FORWARD;
+        }
         if (ConnectContext.get().getSessionVariable().getForwardToMaster()) {
             return RedirectStatus.FORWARD_NO_SYNC;
         } else {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 07a97fe1e1d..a1267228f53 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -172,6 +172,7 @@ import 
org.apache.doris.nereids.DorisParser.SelectClauseContext;
 import org.apache.doris.nereids.DorisParser.SelectColumnClauseContext;
 import org.apache.doris.nereids.DorisParser.SelectHintContext;
 import org.apache.doris.nereids.DorisParser.SetOperationContext;
+import org.apache.doris.nereids.DorisParser.ShowConfigContext;
 import org.apache.doris.nereids.DorisParser.ShowConstraintContext;
 import org.apache.doris.nereids.DorisParser.ShowCreateMTMVContext;
 import org.apache.doris.nereids.DorisParser.ShowCreateProcedureContext;
@@ -390,6 +391,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.LoadCommand;
 import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
 import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
 import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand;
@@ -472,6 +474,7 @@ import org.apache.doris.policy.FilterType;
 import org.apache.doris.policy.PolicyTypeEnum;
 import org.apache.doris.qe.ConnectContext;
 import org.apache.doris.qe.SqlModeHelper;
+import org.apache.doris.system.NodeType;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableMap;
@@ -3669,4 +3672,24 @@ public class LogicalPlanBuilder extends 
DorisParserBaseVisitor<Object> {
                 rollupNames, withAllRollUp);
         return new CreateTableLikeCommand(info);
     }
+
+    @Override
+    public LogicalPlan visitShowConfig(ShowConfigContext ctx) {
+        ShowConfigCommand command;
+        if (ctx.type.getText().equalsIgnoreCase(NodeType.FRONTEND.name())) {
+            command = new ShowConfigCommand(NodeType.FRONTEND);
+        } else {
+            command = new ShowConfigCommand(NodeType.BACKEND);
+        }
+        if (ctx.LIKE() != null && ctx.pattern != null) {
+            Like like = new Like(new UnboundSlot("ProcedureName"), 
getExpression(ctx.pattern));
+            String pattern = ((Literal) like.child(1)).getStringValue();
+            command.setPattern(pattern);
+        }
+        if (ctx.FROM() != null && ctx.backendId != null) {
+            long backendId = Long.parseLong(ctx.backendId.getText());
+            command.setBackendId(backendId);
+        }
+        return command;
+    }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
index e4f0f4c102e..9f451732bdc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java
@@ -163,5 +163,6 @@ public enum PlanType {
     CREATE_TABLE_LIKE_COMMAND,
 
     PREPARED_COMMAND,
-    EXECUTE_COMMAND
+    EXECUTE_COMMAND,
+    SHOW_CONFIG_COMMAND
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowConfigCommand.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowConfigCommand.java
new file mode 100644
index 00000000000..b139d0a5772
--- /dev/null
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowConfigCommand.java
@@ -0,0 +1,171 @@
+// 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.nereids.trees.plans.commands;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.CaseSensibility;
+import org.apache.doris.common.ConfigBase;
+import org.apache.doris.common.PatternMatcher;
+import org.apache.doris.common.PatternMatcherWrapper;
+import org.apache.doris.nereids.trees.plans.PlanType;
+import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
+import org.apache.doris.qe.ConnectContext;
+import org.apache.doris.qe.ShowResultSet;
+import org.apache.doris.qe.ShowResultSetMetaData;
+import org.apache.doris.qe.StmtExecutor;
+import org.apache.doris.system.Backend;
+import org.apache.doris.system.NodeType;
+import org.apache.doris.system.SystemInfoService;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import org.json.JSONArray;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * show frontend/backend config
+*/
+public class ShowConfigCommand extends Command implements NoForward {
+
+    public static final ImmutableList<String> FE_TITLE_NAMES = new 
ImmutableList.Builder<String>().add("Key").add(
+            
"Value").add("Type").add("IsMutable").add("MasterOnly").add("Comment").build();
+    public static final ImmutableList<String> BE_TITLE_NAMES = new 
ImmutableList.Builder<String>().add("BackendId")
+            
.add("Host").add("Key").add("Value").add("Type").add("IsMutable").build();
+
+    private final NodeType nodeType;
+    private String pattern;
+    private long backendId;
+    private boolean isShowSingleBackend;
+
+    public ShowConfigCommand(NodeType nodeType) {
+        super(PlanType.SHOW_CONFIG_COMMAND);
+        this.nodeType = nodeType;
+    }
+
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    public void setBackendId(long backendId) {
+        this.backendId = backendId;
+        this.isShowSingleBackend = true;
+    }
+
+    private ShowResultSetMetaData getMetaData(ImmutableList<String> metaNames) 
{
+        ShowResultSetMetaData.Builder builder = 
ShowResultSetMetaData.builder();
+        for (String title : metaNames) {
+            builder.addColumn(new Column(title, 
ScalarType.createStringType()));
+        }
+        return builder.build();
+    }
+
+    private ShowResultSet handShowFrontendConfig() throws AnalysisException {
+        List<List<String>> results;
+        PatternMatcher matcher = null;
+        if (pattern != null) {
+            matcher = PatternMatcherWrapper.createMysqlPattern(pattern, 
CaseSensibility.CONFIG.getCaseSensibility());
+        }
+        results = ConfigBase.getConfigInfo(matcher);
+        // Sort all configs by config key.
+        results.sort(Comparator.comparing(o -> o.get(0)));
+        return new ShowResultSet(getMetaData(FE_TITLE_NAMES), results);
+    }
+
+    private ShowResultSet handShowBackendConfig() throws AnalysisException {
+        List<List<String>> results = new ArrayList<>();
+        List<Long> backendIds;
+        final SystemInfoService systemInfoService = Env.getCurrentSystemInfo();
+        if (isShowSingleBackend) {
+            if (systemInfoService.getBackend(backendId) == null) {
+                throw new AnalysisException("Backend " + backendId + " not 
exists");
+            }
+            Backend backend = systemInfoService.getBackend(backendId);
+            if (!backend.isAlive()) {
+                throw new AnalysisException("Backend " + backendId + " is not 
alive");
+            }
+            backendIds = Lists.newArrayList(backendId);
+        } else {
+            backendIds = systemInfoService.getAllBackendIds(true);
+        }
+
+        PatternMatcher matcher = null;
+        if (pattern != null) {
+            matcher = PatternMatcherWrapper.createMysqlPattern(pattern, 
CaseSensibility.CONFIG.getCaseSensibility());
+        }
+        for (long beId : backendIds) {
+            Backend backend = systemInfoService.getBackend(beId);
+            String host = backend.getHost();
+            int httpPort = backend.getHttpPort();
+            String urlString = String.format("http://%s:%d/api/show_config";, 
host, httpPort);
+            try {
+                URL url = new URL(urlString);
+                URLConnection urlConnection = url.openConnection();
+                InputStream inputStream = urlConnection.getInputStream();
+                BufferedReader reader = new BufferedReader(new 
InputStreamReader(inputStream));
+                while (reader.ready()) {
+                    // line's format like [["k1","v1"], ["k2","v2"]]
+                    String line = reader.readLine();
+                    JSONArray outer = new JSONArray(line);
+                    for (int i = 0; i < outer.length(); ++i) {
+                        // [key, type, value, isMutable]
+                        JSONArray inner = outer.getJSONArray(i);
+                        if (matcher == null || 
matcher.match(inner.getString(0))) {
+                            List<String> rows = Lists.newArrayList();
+                            rows.add(String.valueOf(beId));
+                            rows.add(host);
+                            rows.add(inner.getString(0));  // key
+                            rows.add(inner.getString(2));  // value
+                            rows.add(inner.getString(1));  // Type
+                            rows.add(inner.getString(3));  // isMutable
+                            results.add(rows);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                throw new AnalysisException(
+                    String.format("Can’t get backend config, backendId: %d, 
host: %s", beId, host));
+            }
+        }
+        return new ShowResultSet(getMetaData(BE_TITLE_NAMES), results);
+    }
+
+    @Override
+    public void run(ConnectContext ctx, StmtExecutor executor) throws 
Exception {
+        if (nodeType == NodeType.FRONTEND) {
+            executor.sendResultSet(handShowFrontendConfig());
+        } else {
+            executor.sendResultSet(handShowBackendConfig());
+        }
+    }
+
+    @Override
+    public <R, C> R accept(PlanVisitor<R, C> visitor, C context) {
+        return visitor.visitShowConfigCommand(this, context);
+    }
+}
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
index 541dbd49849..a180505d42e 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java
@@ -41,6 +41,7 @@ import 
org.apache.doris.nereids.trees.plans.commands.LoadCommand;
 import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand;
 import org.apache.doris.nereids.trees.plans.commands.RefreshMTMVCommand;
 import org.apache.doris.nereids.trees.plans.commands.ResumeMTMVCommand;
+import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand;
 import org.apache.doris.nereids.trees.plans.commands.ShowCreateMTMVCommand;
 import 
org.apache.doris.nereids.trees.plans.commands.ShowCreateProcedureCommand;
@@ -186,4 +187,8 @@ public interface CommandVisitor<R, C> {
     default R visitCreateTableLikeCommand(CreateTableLikeCommand 
createTableLikeCommand, C context) {
         return visitCommand(createTableLikeCommand, context);
     }
+
+    default R visitShowConfigCommand(ShowConfigCommand showConfigCommand, C 
context) {
+        return visitCommand(showConfigCommand, context);
+    }
 }
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 56a88bbd650..f5dd6e441a9 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
@@ -232,6 +232,7 @@ import org.apache.doris.statistics.query.QueryStatsUtil;
 import org.apache.doris.statistics.util.StatisticsUtil;
 import org.apache.doris.system.Backend;
 import org.apache.doris.system.Diagnoser;
+import org.apache.doris.system.NodeType;
 import org.apache.doris.system.SystemInfoService;
 import org.apache.doris.task.AgentBatchTask;
 import org.apache.doris.task.AgentClient;
@@ -258,6 +259,7 @@ import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.logging.log4j.LogManager;
 import org.apache.logging.log4j.Logger;
+import org.json.JSONArray;
 
 import java.io.BufferedReader;
 import java.io.InputStream;
@@ -2389,17 +2391,80 @@ public class ShowExecutor {
 
     private void handleAdminShowConfig() throws AnalysisException {
         ShowConfigStmt showStmt = (ShowConfigStmt) stmt;
-        List<List<String>> results;
+        if (showStmt.getType() == NodeType.FRONTEND) {
+            List<List<String>> results;
+            PatternMatcher matcher = null;
+            if (showStmt.getPattern() != null) {
+                matcher = 
PatternMatcherWrapper.createMysqlPattern(showStmt.getPattern(),
+                        CaseSensibility.CONFIG.getCaseSensibility());
+            }
+            results = ConfigBase.getConfigInfo(matcher);
+            // Sort all configs by config key.
+            results.sort(Comparator.comparing(o -> o.get(0)));
+            resultSet = new ShowResultSet(showStmt.getMetaData(), results);
+        } else {
+            handShowBackendConfig(showStmt);
+        }
+    }
+
+    private void handShowBackendConfig(ShowConfigStmt stmt) throws 
AnalysisException {
+        List<List<String>> results = new ArrayList<>();
+        List<Long> backendIds;
+        final SystemInfoService systemInfoService = Env.getCurrentSystemInfo();
+        if (stmt.isShowSingleBackend()) {
+            long backendId = stmt.getBackendId();
+            if (systemInfoService.getBackend(backendId) == null) {
+                throw new AnalysisException("Backend " + backendId + " not 
exists");
+            }
+            Backend backend = systemInfoService.getBackend(backendId);
+            if (!backend.isAlive()) {
+                throw new AnalysisException("Backend " + backendId + " is not 
alive");
+            }
+            backendIds = Lists.newArrayList(backendId);
+        } else {
+            backendIds = systemInfoService.getAllBackendIds(true);
+        }
 
         PatternMatcher matcher = null;
-        if (showStmt.getPattern() != null) {
-            matcher = 
PatternMatcherWrapper.createMysqlPattern(showStmt.getPattern(),
+        if (stmt.getPattern() != null) {
+            matcher = 
PatternMatcherWrapper.createMysqlPattern(stmt.getPattern(),
                     CaseSensibility.CONFIG.getCaseSensibility());
         }
-        results = ConfigBase.getConfigInfo(matcher);
-        // Sort all configs by config key.
-        results.sort(Comparator.comparing(o -> o.get(0)));
-        resultSet = new ShowResultSet(showStmt.getMetaData(), results);
+        for (long beId : backendIds) {
+            Backend backend = systemInfoService.getBackend(beId);
+            String host = backend.getHost();
+            int httpPort = backend.getHttpPort();
+            String urlString = String.format("http://%s:%d/api/show_config";, 
host, httpPort);
+            try {
+                URL url = new URL(urlString);
+                URLConnection urlConnection = url.openConnection();
+                InputStream inputStream = urlConnection.getInputStream();
+                BufferedReader reader = new BufferedReader(new 
InputStreamReader(inputStream));
+                while (reader.ready()) {
+                    // line's format like [["k1","v1"], ["k2","v2"]]
+                    String line = reader.readLine();
+                    JSONArray outer = new JSONArray(line);
+                    for (int i = 0; i < outer.length(); ++i) {
+                        // [key, type, value, isMutable]
+                        JSONArray inner = outer.getJSONArray(i);
+                        if (matcher == null || 
matcher.match(inner.getString(0))) {
+                            List<String> rows = Lists.newArrayList();
+                            rows.add(String.valueOf(beId));
+                            rows.add(host);
+                            rows.add(inner.getString(0));  // key
+                            rows.add(inner.getString(2));  // value
+                            rows.add(inner.getString(1));  // Type
+                            rows.add(inner.getString(3));  // isMutable
+                            results.add(rows);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                throw new AnalysisException(
+                        String.format("Can’t get backend config, backendId: 
%d, host: %s", beId, host));
+            }
+        }
+        resultSet = new ShowResultSet(stmt.getMetaData(), results);
     }
 
     private void handleShowSmallFiles() throws AnalysisException {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/system/NodeType.java 
b/fe/fe-core/src/main/java/org/apache/doris/system/NodeType.java
new file mode 100644
index 00000000000..f101fea3141
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/system/NodeType.java
@@ -0,0 +1,23 @@
+// 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.system;
+
+public enum NodeType {
+    FRONTEND,
+    BACKEND
+}
diff --git a/regression-test/suites/show_p0/test_show_backend_config.groovy 
b/regression-test/suites/show_p0/test_show_backend_config.groovy
new file mode 100644
index 00000000000..b2e0bea26c1
--- /dev/null
+++ b/regression-test/suites/show_p0/test_show_backend_config.groovy
@@ -0,0 +1,57 @@
+// 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.
+
+suite("test_show_backend_config") {
+    
+    def userName = "test_show_backends_config_user"
+    def passwd = "12345"
+
+    sql """drop user if exists ${userName}"""
+    sql """create user ${userName} identified by '${passwd}'"""
+    sql """grant ADMIN_PRIV on *.*.* to ${userName}"""
+
+    def checkResult = {results, beId, bePort -> 
+        for (def row in results) {
+            if (row.BackendId == beId && row.Key == "be_port") {
+                assertEquals(bePort, row.Value);
+                break;
+            }
+        }
+    }
+
+    connect(user = userName, password = passwd, url = context.config.jdbcUrl) {
+        def backends = sql_return_maparray """ show backends """
+        def beId = backends[0].BackendId
+        def bePort = backends[0].BePort
+
+        def result1 = sql_return_maparray """show backend config"""
+        checkResult result1, beId, bePort
+
+        // test with pattern
+        def result2 = sql_return_maparray """show backend config like 
'be_port' """
+        checkResult result2, beId, bePort
+
+        // test from beId
+        def result3 = sql_return_maparray """show backend config from ${beId} 
"""
+        checkResult result3, beId, bePort
+
+        // test from beId with pattern
+        def result4 = sql_return_maparray """show backend config like 
'be_port' from ${beId}"""
+        checkResult result4, beId, bePort
+    }
+}
+


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]


Reply via email to