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

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


The following commit(s) were added to refs/heads/branch-3.0 by this push:
     new 821322c4e22 [branch-3.0](show profile) show query/load profile on 
branch-3.0 (#46615)
821322c4e22 is described below

commit 821322c4e22d3c19b0937e3afbe601985ba7fb0b
Author: zhiqiang <[email protected]>
AuthorDate: Thu Jan 9 22:21:49 2025 +0800

    [branch-3.0](show profile) show query/load profile on branch-3.0 (#46615)
---
 .../antlr4/org/apache/doris/nereids/DorisParser.g4 |  4 +-
 fe/fe-core/src/main/cup/sql_parser.cup             | 17 ++++--
 .../apache/doris/analysis/ShowLoadProfileStmt.java | 70 ++++++++++++++++-----
 .../doris/analysis/ShowQueryProfileStmt.java       | 71 +++++++++++++++++-----
 .../org/apache/doris/common/profile/Profile.java   | 10 +--
 .../doris/common/profile/ProfileManager.java       | 35 +++++++++--
 .../java/org/apache/doris/qe/ShowExecutor.java     | 22 +++----
 .../suites/query_profile/show_profile.groovy       | 60 ++++++++++++++++++
 8 files changed, 229 insertions(+), 60 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 53e79f0bb28..177f494905f 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
@@ -311,8 +311,8 @@ unsupportedShowStatement
         (FROM |IN) tableName=multipartIdentifier
         ((FROM | IN) database=multipartIdentifier)?                            
     #showIndex
     | SHOW TRANSACTION ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showTransaction
-    | SHOW QUERY PROFILE queryIdPath=STRING_LITERAL                            
     #showQueryProfile
-    | SHOW LOAD PROFILE loadIdPath=STRING_LITERAL                              
     #showLoadProfile
+    | SHOW QUERY PROFILE queryIdPath=STRING_LITERAL? limitClause?              
     #showQueryProfile
+    | SHOW LOAD PROFILE loadIdPath=STRING_LITERAL?   limitClause?              
     #showLoadProfile
     | SHOW CACHE HOTSPOT tablePath=STRING_LITERAL                              
     #showCacheHotSpot
     | SHOW ENCRYPTKEYS ((FROM | IN) database=multipartIdentifier)? wildWhere?  
     #showEncryptKeys
     | SHOW SYNC JOB ((FROM | IN) database=multipartIdentifier)?                
     #showSyncJob
diff --git a/fe/fe-core/src/main/cup/sql_parser.cup 
b/fe/fe-core/src/main/cup/sql_parser.cup
index 07219e6f756..c1c5b0f4fb5 100644
--- a/fe/fe-core/src/main/cup/sql_parser.cup
+++ b/fe/fe-core/src/main/cup/sql_parser.cup
@@ -858,6 +858,7 @@ nonterminal String opt_job_ends;
 nonterminal String job_at_time;
 nonterminal ColocateGroupName colocate_group_name;
 nonterminal TableScanParams opt_scan_params_ref;
+nonterminal String opt_profile_path;
 
 nonterminal LoadTask.MergeType opt_merge_type, opt_with_merge_type;
 
@@ -1546,6 +1547,14 @@ quantity ::=
     :}
     ;
 
+opt_profile_path ::=
+    /* empty */
+    | STRING_LITERAL:queryIdPath
+    {:
+        RESULT = queryIdPath;
+    :}
+    ;
+
 opt_user ::=
     /* empty */
     | KW_FOR user:user
@@ -4646,13 +4655,13 @@ show_param ::=
     {:
         RESULT = new ShowTransactionStmt(dbName, parser.where);
     :}
-    | KW_QUERY KW_PROFILE STRING_LITERAL:queryIdPath
+    | KW_QUERY KW_PROFILE opt_profile_path:queryIdPath limit_clause:limitClause
     {:
-        RESULT = new ShowQueryProfileStmt(queryIdPath);
+        RESULT = new ShowQueryProfileStmt(queryIdPath, limitClause);
     :}
-    | KW_LOAD KW_PROFILE STRING_LITERAL:loadIdPath
+    | KW_LOAD KW_PROFILE opt_profile_path:loadIdPath limit_clause:limitClause
     {:
-        RESULT = new ShowLoadProfileStmt(loadIdPath);
+        RESULT = new ShowLoadProfileStmt(loadIdPath, limitClause);
     :}
     | KW_CACHE KW_HOTSPOT STRING_LITERAL:tablePath
     {:
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadProfileStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadProfileStmt.java
index 78437cec796..b4245e4e9f0 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadProfileStmt.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowLoadProfileStmt.java
@@ -17,32 +17,70 @@
 
 package org.apache.doris.analysis;
 
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.Config;
-import org.apache.doris.common.UserException;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.profile.SummaryProfile;
 import org.apache.doris.qe.ShowResultSetMetaData;
 
-// deprecated stmt, use will be guided to a specific url to get profile from
-// web browser
+import com.google.common.base.Strings;
+
+// For stmt like:
+// show load profile "/" limit 10;
 public class ShowLoadProfileStmt extends ShowStmt implements 
NotFallbackInParser {
-    private String loadIdPath;
+    // This should be same as ProfileManager.PROFILE_HEADERS
+    public static final ShowResultSetMetaData META_DATA_QUERY_IDS;
+    private static final long DefaultLimit = 20;
+
+    static {
+        ShowResultSetMetaData.Builder builder = 
ShowResultSetMetaData.builder();
+        for (String key : SummaryProfile.SUMMARY_KEYS) {
+            if (key.equals(SummaryProfile.DISTRIBUTED_PLAN)) {
+                continue;
+            }
+            builder.addColumn(new Column(key, ScalarType.createStringType()));
+        }
+        META_DATA_QUERY_IDS = builder.build();
+    }
 
-    public ShowLoadProfileStmt(String path) {
-        this.loadIdPath = path;
+    public ShowLoadProfileStmt(String useless, LimitElement limit) {
+        this.path = useless;
+        this.limitElement = limit;
     }
 
+    private String path;
+    private final LimitElement limitElement;
+
     @Override
-    public void analyze(Analyzer analyzer) throws UserException {
-        String selfHost = Env.getCurrentEnv().getSelfNode().getHost();
-        int httpPort = Config.http_port;
-        String terminalMsg = String.format(
-                "try visit http://%s:%d/QueryProfile/%s, show query/load 
profile syntax is a deprecated feature",
-                selfHost, httpPort, this.loadIdPath);
-        throw new UserException(terminalMsg);
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SHOW LOAD PROFILE");
+        if (!Strings.isNullOrEmpty(path)) {
+            sb.append(" ");
+            sb.append(path);
+        }
+
+        if (limitElement != null && limitElement.getLimit() != -1) {
+            sb.append(" LIMIT ").append(getLimit());
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        return toSql();
     }
 
     @Override
     public ShowResultSetMetaData getMetaData() {
-        return null;
+        return META_DATA_QUERY_IDS;
+    }
+
+    public long getLimit() {
+        if (limitElement == null) {
+            return DefaultLimit;
+        } else {
+            return limitElement.getLimit() == -1 ? DefaultLimit : 
limitElement.getLimit();
+        }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowQueryProfileStmt.java 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowQueryProfileStmt.java
index ad664652d53..296cb1102fc 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowQueryProfileStmt.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowQueryProfileStmt.java
@@ -17,32 +17,71 @@
 
 package org.apache.doris.analysis;
 
-import org.apache.doris.catalog.Env;
-import org.apache.doris.common.Config;
-import org.apache.doris.common.UserException;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ScalarType;
+import org.apache.doris.common.profile.SummaryProfile;
 import org.apache.doris.qe.ShowResultSetMetaData;
 
-// deprecated stmt, use will be guided to a specific url to get profile from
-// web browser
+import com.google.common.base.Strings;
+
+
+// For stmt like:
+// show query profile "/";   # list all saving query ids
 public class ShowQueryProfileStmt extends ShowStmt implements 
NotFallbackInParser {
-    private String queryIdPath;
+    // This should be same as ProfileManager.PROFILE_HEADERS
+    public static final ShowResultSetMetaData META_DATA_QUERY_IDS;
+    private static final long DefaultLimit = 20;
+
+    static {
+        ShowResultSetMetaData.Builder builder = 
ShowResultSetMetaData.builder();
+        for (String key : SummaryProfile.SUMMARY_KEYS) {
+            if (key.equals(SummaryProfile.DISTRIBUTED_PLAN)) {
+                continue;
+            }
+            builder.addColumn(new Column(key, ScalarType.createStringType()));
+        }
+        META_DATA_QUERY_IDS = builder.build();
+    }
+
+    public ShowQueryProfileStmt(String useless, LimitElement limit) {
+        this.path = useless;
+        this.limitElement = limit;
+    }
 
-    public ShowQueryProfileStmt(String queryIdPath) {
-        this.queryIdPath = queryIdPath;
+    private final String path;
+    private final LimitElement limitElement;
+
+    @Override
+    public String toSql() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("SHOW QUERY PROFILE");
+        if (!Strings.isNullOrEmpty(path)) {
+            sb.append(" ");
+            sb.append(path);
+        }
+
+        if (limitElement != null && limitElement.getLimit() != -1) {
+            sb.append(" LIMIT ").append(getLimit());
+        }
+
+        return sb.toString();
     }
 
     @Override
-    public void analyze(Analyzer analyzer) throws UserException {
-        String selfHost = Env.getCurrentEnv().getSelfNode().getHost();
-        int httpPort = Config.http_port;
-        String terminalMsg = String.format(
-                "try visit http://%s:%d/QueryProfile/%s, show query/load 
profile syntax is a deprecated feature",
-                selfHost, httpPort, this.queryIdPath);
-        throw new UserException(terminalMsg);
+    public String toString() {
+        return toSql();
     }
 
     @Override
     public ShowResultSetMetaData getMetaData() {
-        return null;
+        return META_DATA_QUERY_IDS;
+    }
+
+    public long getLimit() {
+        if (limitElement == null) {
+            return DefaultLimit;
+        } else {
+            return limitElement.getLimit() == -1 ? DefaultLimit : 
limitElement.getLimit();
+        }
     }
 }
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/profile/Profile.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/profile/Profile.java
index b52179c48ee..9b54695f225 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/common/profile/Profile.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/common/profile/Profile.java
@@ -81,22 +81,22 @@ public class Profile {
     // profile file name format: time_id
     private static final String SEPERATOR = "_";
 
-    // id will be assgined to id of SummaryProfile.
-    // For broker load, its SummaryPRofile id is a string representation of a 
long integer,
+    // id will be assigned to id of SummaryProfile.
+    // For broker load, its SummaryProfile id is a string representation of a 
long integer,
     // for others, it is queryID
     private String id = "";
     // summaryProfile will be serialized to storage as JSON, and we can 
recover it from storage
     // recover of SummaryProfile is important, because it contains the meta 
information of the profile
     // we need it to construct memory index for profile retrieving.
     private SummaryProfile summaryProfile = new SummaryProfile();
-    // executionProfiles will be stored to storage as text, when geting 
profile content, we will read
+    // executionProfiles will be stored to storage as text, when getting 
profile content, we will read
     // from storage directly.
     private List<ExecutionProfile> executionProfiles = Lists.newArrayList();
     // profileStoragePath will only be assigned when:
     // 1. profile is stored to storage
     // 2. or profile is loaded from storage
     private String profileStoragePath = "";
-    // isQueryFinished means the coordinator or stmtexecutor is finished.
+    // isQueryFinished means the coordinator or stmt executor is finished.
     // does not mean the profile report has finished, since the report is 
async.
     // finish of collection of profile is marked by isCompleted of 
ExecutionProfiles.
     private boolean isQueryFinished = false;
@@ -274,7 +274,7 @@ public class Profile {
     }
 
     // This API will also add the profile to ProfileManager, so that we could 
get the profile from ProfileManager.
-    // isFinished ONLY means the coordinator or stmtexecutor is finished.
+    // isFinished ONLY means the coordinator or stmt executor is finished.
     public synchronized void updateSummary(Map<String, String> summaryInfo, 
boolean isFinished,
             Planner planner) {
         try {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/common/profile/ProfileManager.java 
b/fe/fe-core/src/main/java/org/apache/doris/common/profile/ProfileManager.java
index 981d025792c..ef9f2f7bbaf 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/common/profile/ProfileManager.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/common/profile/ProfileManager.java
@@ -120,8 +120,8 @@ public class ProfileManager extends MasterDaemon {
         }
     }
 
-    // this variable is assgiened to true the first time the profile is loaded 
from storage
-    // no futher write operaiton, so no data race
+    // this variable is assigned to true the first time the profile is loaded 
from storage
+    // no further write operation, so no data race
     boolean isProfileLoaded = false;
 
     // only protect queryIdDeque; queryIdToProfileMap is concurrent, no need 
to protect
@@ -129,10 +129,10 @@ public class ProfileManager extends MasterDaemon {
     private ReadLock readLock;
     private WriteLock writeLock;
 
-    // profile id is long string for brocker load
+    // profile id is long string for broker load
     // is TUniqueId for others.
     private Map<String, ProfileElement> queryIdToProfileMap;
-    // Sometimes one Profile is related with multiple execution 
profiles(Brokerload), so that
+    // Sometimes one Profile is related with multiple execution 
profiles(Broker-load), so that
     // execution profile's query id is not related with Profile's query id.
     private Map<TUniqueId, ExecutionProfile> queryIdToExecutionProfiles;
 
@@ -151,7 +151,7 @@ public class ProfileManager extends MasterDaemon {
         return INSTANCE;
     }
 
-    // The visiablity of ProfileManager() is package level, so that we can 
write ut for it.
+    // The visibility of ProfileManager() is package level, so that we can 
write ut for it.
     ProfileManager() {
         super("profile-manager", Config.profile_manager_gc_interval_seconds * 
1000);
         lock = new ReentrantReadWriteLock(true);
@@ -942,4 +942,29 @@ public class ProfileManager extends MasterDaemon {
             }
         }
     }
+
+    public List<List<String>> getProfileMetaWithType(ProfileType profileType, 
long limit) {
+        List<List<String>> result = Lists.newArrayList();
+        readLock.lock();
+
+        try {
+            PriorityQueue<ProfileElement> queueIdDeque = 
getProfileOrderByQueryFinishTimeDesc();
+            while (!queueIdDeque.isEmpty() && limit > 0) {
+                ProfileElement profileElement = queueIdDeque.poll();
+                Map<String, String> infoStrings = profileElement.infoStrings;
+                if 
(infoStrings.get(SummaryProfile.TASK_TYPE).equals(profileType.toString())) {
+                    List<String> row = Lists.newArrayList();
+                    for (String str : SummaryProfile.SUMMARY_KEYS) {
+                        row.add(infoStrings.get(str));
+                    }
+                    result.add(row);
+                    limit--;
+                }
+            }
+        } finally {
+            readLock.unlock();
+        }
+
+        return result;
+    }
 }
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 babfe4e2265..0fda1f4e5c2 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
@@ -186,6 +186,8 @@ import org.apache.doris.common.proc.SchemaChangeProcDir;
 import org.apache.doris.common.proc.TabletsProcDir;
 import org.apache.doris.common.proc.TrashProcDir;
 import org.apache.doris.common.proc.TrashProcNode;
+import org.apache.doris.common.profile.ProfileManager;
+import org.apache.doris.common.profile.ProfileManager.ProfileType;
 import org.apache.doris.common.util.DebugUtil;
 import org.apache.doris.common.util.ListComparator;
 import org.apache.doris.common.util.LogBuilder;
@@ -2619,21 +2621,17 @@ public class ShowExecutor {
     }
 
     private void handleShowQueryProfile() throws AnalysisException {
-        String selfHost = Env.getCurrentEnv().getSelfNode().getHost();
-        int httpPort = Config.http_port;
-        String terminalMsg = String.format(
-                "try visit http://%s:%d/QueryProfile, show query/load profile 
syntax is a deprecated feature",
-                selfHost, httpPort);
-        throw new AnalysisException(terminalMsg);
+        ShowQueryProfileStmt showStmt = (ShowQueryProfileStmt) stmt;
+        List<List<String>> rows = 
ProfileManager.getInstance().getProfileMetaWithType(
+                                                                
ProfileType.QUERY, showStmt.getLimit());
+        resultSet = new ShowResultSet(showStmt.getMetaData(), rows);
     }
 
     private void handleShowLoadProfile() throws AnalysisException {
-        String selfHost = Env.getCurrentEnv().getSelfNode().getHost();
-        int httpPort = Config.http_port;
-        String terminalMsg = String.format(
-                "try visit http://%s:%d/QueryProfile, show query/load profile 
syntax is a deprecated feature",
-                selfHost, httpPort);
-        throw new AnalysisException(terminalMsg);
+        ShowLoadProfileStmt showStmt = (ShowLoadProfileStmt) stmt;
+        List<List<String>> rows = 
ProfileManager.getInstance().getProfileMetaWithType(
+                                                                
ProfileType.LOAD, showStmt.getLimit());
+        resultSet = new ShowResultSet(showStmt.getMetaData(), rows);
     }
 
     private void handleShowCreateRepository() throws AnalysisException {
diff --git a/regression-test/suites/query_profile/show_profile.groovy 
b/regression-test/suites/query_profile/show_profile.groovy
new file mode 100644
index 00000000000..e0762f531e5
--- /dev/null
+++ b/regression-test/suites/query_profile/show_profile.groovy
@@ -0,0 +1,60 @@
+// 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('show_profile') {
+    sql "set enable_profile=true;"
+    sql "drop table if exists show_profile"
+    sql "create table show_profile (id int, name varchar(32)) distributed by 
hash(id) buckets 1 properties('replication_num'='1');"
+    sql "insert into show_profile values(1, 'a'), (2, 'b'), (3, 'c');"
+
+    for (int i = 0; i < 10; i++) {
+        sql "select * from show_profile;"
+    }
+
+    boolean executedFailed = false
+    try {
+        sql "show query profile;"
+        sql "show load profile;"
+    } catch (Exception e) {
+        logger.error("show query profile failed ", e)
+        executedFailed = true
+    }
+
+    assertEquals(false, executedFailed)
+
+    try {
+        sql """show query profile "/";"""
+        sql """show load profile "/";"""
+        executedFailed = false
+    } catch (Exception e) {
+        logger.error("show profile failed: {}", e)
+        executedFailed = true
+    }
+
+    assertEquals(false, executedFailed)
+
+    try {
+        sql """show query profile "/" limit 10;"""
+        sql """show load profile "/" limit 10;"""
+        executedFailed = false
+    } catch (Exception e) {
+        logger.error("show profile failed: {}", e)
+        executedFailed = true
+    }
+
+    assertEquals(false, executedFailed)
+}
\ No newline at end of file


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

Reply via email to