You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by dk...@apache.org on 2022/03/08 12:47:54 UTC

[sling-org-apache-sling-app-cms] branch master updated: SLING-11102 - Adding execution support to query debug console

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

dklco pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-app-cms.git


The following commit(s) were added to refs/heads/master by this push:
     new e100237  SLING-11102 - Adding execution support to query debug console
e100237 is described below

commit e1002375c7728ee3f532361a911b6dca14bdccf7
Author: Dan Klco <kl...@adobe.com>
AuthorDate: Tue Mar 8 07:47:30 2022 -0500

    SLING-11102 - Adding execution support to query debug console
---
 .../sling/cms/core/models/QueryDebugger.java       | 76 ++++++++++++++++++++--
 .../components/cms/querydebug/querydebug.jsp       | 45 +++++++++----
 .../libs/sling-cms/content/admin/querydebug.json   | 36 ++++++++++
 3 files changed, 138 insertions(+), 19 deletions(-)

diff --git a/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java b/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
index 7ce7012..e367b5f 100644
--- a/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
+++ b/core/src/main/java/org/apache/sling/cms/core/models/QueryDebugger.java
@@ -30,7 +30,9 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryManager;
+import javax.jcr.query.QueryResult;
 import javax.jcr.query.Row;
+import javax.jcr.query.RowIterator;
 import javax.management.AttributeNotFoundException;
 import javax.management.InstanceNotFoundException;
 import javax.management.MBeanException;
@@ -42,6 +44,7 @@ import javax.management.openmbean.CompositeData;
 import javax.management.openmbean.TabularData;
 
 import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.models.annotations.Model;
 import org.apache.sling.models.annotations.injectorspecific.Self;
 import org.osgi.annotation.versioning.ProviderType;
@@ -56,10 +59,14 @@ public class QueryDebugger {
 
     private static final Logger log = LoggerFactory.getLogger(QueryDebugger.class);
     private final String plan;
+    private final long duration;
+    private final long estimatedSize;
     private final String exception;
+    private final List<Resource> results = new ArrayList<>();
     private final String statement;
     private final List<Map<String, Object>> slowQueries = new ArrayList<>();
     private final List<Map<String, Object>> popularQueries = new ArrayList<>();
+    private final boolean enabled;
 
     @Inject
     public QueryDebugger(@Self SlingHttpServletRequest request) {
@@ -67,6 +74,11 @@ public class QueryDebugger {
         Optional<String> statementParam = Optional.ofNullable(request.getParameter("statement"));
         String language = Optional.ofNullable(request.getParameter("language")).orElse(Query.JCR_SQL2);
 
+        int limit = Optional.ofNullable(request.getParameter("sample")).map(s -> Integer.parseInt(s, 10)).orElse(0);
+
+        boolean lenabled = false;
+        long lestimate = 0;
+        long lduration = -1;
         String lplan = null;
         String lexception = null;
         String lstatement = null;
@@ -75,18 +87,44 @@ public class QueryDebugger {
 
                 QueryManager queryManager = request.getResourceResolver().adaptTo(Session.class).getWorkspace()
                         .getQueryManager();
-                Query query = queryManager.createQuery("explain " + statementParam.get(), language);
-                Row row = query.execute().getRows().nextRow();
+                Query explainQuery = queryManager.createQuery("explain " + statementParam.get(), language);
+                Row row = explainQuery.execute().getRows().nextRow();
                 lplan = row.getValue("plan").getString();
                 lstatement = statementParam.get();
+
+                if (limit > 0) {
+                    lenabled = true;
+                    long start = System.currentTimeMillis();
+                    Query query = queryManager.createQuery(statementParam.get(), language);
+                    query.setLimit(limit);
+                    QueryResult queryResult = query.execute();
+                    RowIterator rowIterator = queryResult.getRows();
+                    lestimate = rowIterator.getSize();
+                    lduration = System.currentTimeMillis() - start;
+                    while (rowIterator.hasNext()) {
+                        Optional.ofNullable(rowIterator.nextRow())
+                                .map(n -> {
+                                    try {
+                                        return request.getResourceResolver().getResource(n.getPath());
+                                    } catch (RepositoryException e) {
+                                        log.warn("Exception getting path from row: {}", n, e);
+                                        return null;
+                                    }
+                                })
+                                .ifPresent(results::add);
+                    }
+                }
             }
         } catch (RepositoryException re) {
             lexception = re.toString();
             log.warn("Failed to debug query: {}", statementParam, re);
         } finally {
             this.plan = lplan;
+            this.duration = lduration;
             this.exception = lexception;
             this.statement = lstatement;
+            this.estimatedSize = lestimate;
+            this.enabled = lenabled;
         }
 
         try {
@@ -110,10 +148,24 @@ public class QueryDebugger {
     }
 
     /**
-     * @return the plan
+     * @return the execution duration or -1
      */
-    public String getPlan() {
-        return plan;
+    public long getDuration() {
+        return duration;
+    }
+
+    /**
+     * @return the estimated size
+     */
+    public long getEstimatedSize() {
+        return estimatedSize;
+    }
+
+    /**
+     * @return if execution enabled
+     */
+    public boolean getExecutionEnabled() {
+        return enabled;
     }
 
     /**
@@ -124,6 +176,20 @@ public class QueryDebugger {
     }
 
     /**
+     * @return the plan
+     */
+    public String getPlan() {
+        return plan;
+    }
+
+    /**
+     * @return the results
+     */
+    public List<Resource> getResults() {
+        return results;
+    }
+
+    /**
      * @return the statement
      */
     public String getStatement() {
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
index fe88692..8bd1f38 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/components/cms/querydebug/querydebug.jsp
@@ -21,18 +21,35 @@
     <sling:adaptTo adaptable="${slingRequest}" adaptTo="org.apache.sling.cms.core.models.QueryDebugger" var="queryDebugger" />
     <br/><hr/><br/>
     <dl>
-    <c:if test="${not empty queryDebugger.statement}">
-        <dt><fmt:message key="Query Statement" /><dt>
-        <dd>${sling:encode(queryDebugger.statement,'HTML')}</dd>
-    </c:if>
-    <c:if test="${not empty queryDebugger.plan}">
-        <dt><fmt:message key="Plan" /><dt>
-        <dd>${sling:encode(queryDebugger.plan,'HTML')}</dd>
-    </c:if>
-    <c:if test="${not empty queryDebugger.exception}">
-        <dt><fmt:message key="Exception" /><dt>
-        <dd>${sling:encode(queryDebugger.exception,'HTML')}</dd>
-    </c:if>
+        <c:if test="${not empty queryDebugger.statement}">
+            <dt><fmt:message key="Query Statement" /><dt>
+            <dd>${sling:encode(queryDebugger.statement,'HTML')}</dd>
+        </c:if>
+        <c:if test="${not empty queryDebugger.plan}">
+            <dt><fmt:message key="Plan" /><dt>
+            <dd>${sling:encode(queryDebugger.plan,'HTML')}</dd>
+        </c:if>
+        <c:if test="${not empty queryDebugger.exception}">
+            <dt><fmt:message key="Exception" /><dt>
+            <dd>${sling:encode(queryDebugger.exception,'HTML')}</dd>
+        </c:if>
+        <c:if test="${queryDebugger.executionEnabled}">
+            <dt><fmt:message key="Duration" /><dt>
+            <dd>${queryDebugger.duration}</dd>
+            <dt><fmt:message key="Size" /><dt>
+            <dd>${queryDebugger.estimatedSize}</dd>
+            <dt><fmt:message key="Results" /><dt>
+            <dd>
+                <nav class="panel fixed-box">
+                    <c:forEach var="result" items="${queryDebugger.results}">
+                        <a class="panel-block" href="/bin/browser.html${result.path}" target="_blank">
+                            ${result.path} [${result.resourceType}]
+                        </a>
+                    </c:forEach>
+                </nav>
+            </dd>
+        </c:if>
+    </dl>
     <br/><hr/><br/>
     <h2><fmt:message key="Popular Queries" /></h2>
     <table class="table">
@@ -59,7 +76,7 @@
                     ${query.position}
                 </td>
                 <td>
-                    ${fn:encode(query.statement,'HTML')}
+                    ${sling:encode(query.statement,'HTML')}
                 </td>
                 <td>
                     ${query.language}
@@ -98,7 +115,7 @@
                     ${query.position}
                 </td>
                 <td>
-                    ${fn:encode(query.statement,'HTML')}
+                    ${sling:encode(query.statement,'HTML')}
                 </td>
                 <td>
                     ${query.language}
diff --git a/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json b/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
index 7e6ebbc..473b956 100644
--- a/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
+++ b/ui/src/main/resources/jcr_root/libs/sling-cms/content/admin/querydebug.json
@@ -54,6 +54,42 @@
                 "value": "sql"
               }
             }
+          },
+          "sampleResults": {
+            "jcr:primaryType": "nt:unstructured",
+            "sling:resourceType": "sling-cms/components/editor/fields/select",
+            "label": "Sample Results",
+            "name": "sample",
+            "options": {
+              "none": {
+                "label": "None",
+                "value": "0"
+              },
+              "100": {
+                "label": "100",
+                "value": "100"
+              },
+              "1000": {
+                "label": "1000",
+                "value": "1000"
+              },
+              "10000": {
+                "label": "10000",
+                "value": "10000"
+              },
+              "100000": {
+                "label": "100000",
+                "value": "100000"
+              },
+              "1000000": {
+                "label": "1000000",
+                "value": "1000000"
+              },
+              "2147483647": {
+                "label": "MAX",
+                "value": "2147483647"
+              }
+            }
           }
         }
       },