You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@drill.apache.org by GitBox <gi...@apache.org> on 2019/01/07 21:48:05 UTC

[GitHub] HanumathRao closed pull request #1593: DRILL-6050: Provide a limit to number of rows fetched for a query in UI

HanumathRao closed pull request #1593: DRILL-6050: Provide a limit to number of rows fetched for a query in UI
URL: https://github.com/apache/drill/pull/1593
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
index ceae237788b..62850682517 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
@@ -224,6 +224,10 @@ private ExecConstants() {
   public static final String HTTP_AUTHENTICATION_MECHANISMS = "drill.exec.http.auth.mechanisms";
   public static final String HTTP_SPNEGO_PRINCIPAL = "drill.exec.http.auth.spnego.principal";
   public static final String HTTP_SPNEGO_KEYTAB = "drill.exec.http.auth.spnego.keytab";
+  //Control Web UI Resultset
+  public static final String HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_CHECKED = "drill.exec.http.web.client.resultset.autolimit.checked";
+  public static final String HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_ROWS = "drill.exec.http.web.client.resultset.autolimit.rows";
+  public static final String HTTP_WEB_CLIENT_RESULTSET_ROWS_PER_PAGE_VALUES = "drill.exec.http.web.client.resultset.rowsPerPageValues";
   //Customize filters in options
   public static final String HTTP_WEB_OPTIONS_FILTERS = "drill.exec.http.web.options.filters";
   public static final String SYS_STORE_PROVIDER_CLASS = "drill.exec.sys.store.provider.class";
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
index 148d2396743..da8ccdc5abf 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/DrillRestServer.java
@@ -313,7 +313,7 @@ public void dispose(WebUserConnection instance) {
      * @return session user principal
      */
     private Principal createSessionUserPrincipal(DrillConfig config, HttpServletRequest request) {
-      if (WebServer.isImpersonationOnlyEnabled(config)) {
+      if (WebServer.isOnlyImpersonationEnabled(config)) {
         final String userName = request.getHeader("User-Name");
         if (!Strings.isNullOrEmpty(userName)) {
           return new DrillUserPrincipal(userName, true);
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
index e585cf94a75..61af7c7acd3 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/QueryResources.java
@@ -18,8 +18,11 @@
 package org.apache.drill.exec.server.rest;
 
 import org.apache.drill.shaded.guava.com.google.common.base.CharMatcher;
+import org.apache.drill.shaded.guava.com.google.common.base.Joiner;
 import org.apache.drill.shaded.guava.com.google.common.collect.ImmutableList;
 import org.apache.drill.shaded.guava.com.google.common.collect.Lists;
+import org.apache.drill.common.config.DrillConfig;
+import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.server.rest.DrillRestServer.UserAuthEnabled;
 import org.apache.drill.exec.server.rest.auth.DrillUserPrincipal;
 import org.apache.drill.exec.server.rest.QueryWrapper.QueryResult;
@@ -36,6 +39,8 @@
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.SecurityContext;
+
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
@@ -55,11 +60,8 @@
   @Produces(MediaType.TEXT_HTML)
   public Viewable getQuery() {
     return ViewableWithPermissions.create(
-        authEnabled.get(),
-        "/rest/query/query.ftl",
-        sc,
-        // if impersonation is enabled without authentication, will provide mechanism to add user name to request header from Web UI
-        WebServer.isImpersonationOnlyEnabled(work.getContext().getConfig()));
+        authEnabled.get(), "/rest/query/query.ftl",
+        sc, new QueryPage(work));
   }
 
   @POST
@@ -85,20 +87,56 @@ public Viewable submitQuery(@FormParam("query") String query,
     try {
       final String trimmedQueryString = CharMatcher.is(';').trimTrailingFrom(query.trim());
       final QueryResult result = submitQueryJSON(new QueryWrapper(trimmedQueryString, queryType));
-
-      return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/result.ftl", sc, new TabularResult(result));
+      List<Integer> rowsPerPageValues = work.getContext().getConfig().getIntList(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_ROWS_PER_PAGE_VALUES);
+      Collections.sort(rowsPerPageValues);
+      final String rowsPerPageValuesAsStr = Joiner.on(",").join(rowsPerPageValues);
+      return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/result.ftl", sc, new TabularResult(result, rowsPerPageValuesAsStr));
     } catch (Exception | Error e) {
       logger.error("Query from Web UI Failed", e);
       return ViewableWithPermissions.create(authEnabled.get(), "/rest/query/errorMessage.ftl", sc, e);
     }
   }
 
+  /**
+   * Model class for Query page
+   */
+  public static class QueryPage {
+    private final boolean onlyImpersonationEnabled;
+    private final boolean autoLimitEnabled;
+    private final int defaultRowsAutoLimited;
+
+    public QueryPage(WorkManager work) {
+      DrillConfig config = work.getContext().getConfig();
+      //if impersonation is enabled without authentication, will provide mechanism to add user name to request header from Web UI
+      onlyImpersonationEnabled = WebServer.isOnlyImpersonationEnabled(config);
+      autoLimitEnabled = config.getBoolean(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_CHECKED);
+      defaultRowsAutoLimited = config.getInt(ExecConstants.HTTP_WEB_CLIENT_RESULTSET_AUTOLIMIT_ROWS);
+    }
+
+    public boolean isOnlyImpersonationEnabled() {
+      return onlyImpersonationEnabled;
+    }
+
+    public boolean isAutoLimitEnabled() {
+      return autoLimitEnabled;
+    }
+
+    public int getDefaultRowsAutoLimited() {
+      return defaultRowsAutoLimited;
+    }
+  }
+
+  /**
+   * Model class for Results page
+   */
   public static class TabularResult {
     private final List<String> columns;
     private final List<List<String>> rows;
     private final String queryId;
+    private final String rowsPerPageValues;
 
-    public TabularResult(QueryResult result) {
+    public TabularResult(QueryResult result, String rowsPerPageValuesAsStr) {
+      rowsPerPageValues = rowsPerPageValuesAsStr;
       queryId = result.getQueryId();
       final List<List<String>> rows = Lists.newArrayList();
       for (Map<String, String> rowMap:result.rows) {
@@ -128,7 +166,11 @@ public String getQueryId() {
     public List<List<String>> getRows() {
       return rows;
     }
-  }
 
+    //Used by results.ftl to render default number of pages per row
+    public String getRowsPerPageValues() {
+      return rowsPerPageValues;
+    }
+  }
 
 }
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
index c02acd2e1cd..689b06bf781 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/WebServer.java
@@ -164,7 +164,7 @@ public WebServer(final BootStrapContext context, final WorkManager workManager,
    * @param config Drill configuration
    * @return true if impersonation without authentication is enabled, false otherwise
    */
-  public static boolean isImpersonationOnlyEnabled(DrillConfig config) {
+  public static boolean isOnlyImpersonationEnabled(DrillConfig config) {
     return !config.getBoolean(ExecConstants.USER_AUTHENTICATION_ENABLED)
         && config.getBoolean(ExecConstants.IMPERSONATION_ENABLED);
   }
@@ -259,7 +259,7 @@ private ServletContextHandler createServletContextHandler(final boolean authEnab
       servletContextHandler.setSessionHandler(createSessionHandler(servletContextHandler.getSecurityHandler()));
     }
 
-    if (isImpersonationOnlyEnabled(workManager.getContext().getConfig())) {
+    if (isOnlyImpersonationEnabled(workManager.getContext().getConfig())) {
       for (String path : new String[]{"/query", "/query.json"}) {
         servletContextHandler.addFilter(UserNameFilter.class, path, EnumSet.of(DispatcherType.REQUEST));
       }
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
index 7e72556bc80..bf071144e24 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/profile/ProfileWrapper.java
@@ -130,7 +130,7 @@ public ProfileWrapper(final QueryProfile profile, DrillConfig drillConfig) {
     }
     this.options = options;
 
-    this.onlyImpersonationEnabled = WebServer.isImpersonationOnlyEnabled(drillConfig);
+    this.onlyImpersonationEnabled = WebServer.isOnlyImpersonationEnabled(drillConfig);
     this.noProgressWarningThreshold = String.valueOf(drillConfig.getInt(ExecConstants.PROFILE_WARNING_PROGRESS_THRESHOLD));
   }
 
diff --git a/exec/java-exec/src/main/resources/drill-module.conf b/exec/java-exec/src/main/resources/drill-module.conf
index ac35cd90d1e..4a5f07549ea 100644
--- a/exec/java-exec/src/main/resources/drill-module.conf
+++ b/exec/java-exec/src/main/resources/drill-module.conf
@@ -167,6 +167,13 @@ drill.exec: {
             maximum: 9223372036854775807
         }
     },
+    web.client.resultset: {
+        autolimit {
+            checked: false,
+            rows: 1000
+        },
+        rowsPerPageValues: [10, 25, 50, 75, 100]
+    },
     web.options.filters: ["planner", "store", "parquet", "hashagg", "hashjoin"]
   },
   //setting javax variables for ssl configurations is being deprecated.
diff --git a/exec/java-exec/src/main/resources/rest/profile/profile.ftl b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
index 6b4e732d87d..e432e208005 100644
--- a/exec/java-exec/src/main/resources/rest/profile/profile.ftl
+++ b/exec/java-exec/src/main/resources/rest/profile/profile.ftl
@@ -23,18 +23,16 @@
 <script src="/static/js/dagre-d3.min.js"></script>
 <script src="/static/js/graph.js"></script>
 <script src="/static/js/jquery.dataTables-1.10.16.min.js"></script>
-<#if model.isOnlyImpersonationEnabled()>
-    <script src="/static/js/jquery.form.js"></script>
-    <script src="/static/js/querySubmission.js"></script>
-</#if>
-  <!-- Ace Libraries for Syntax Formatting -->
-  <script src="/static/js/ace-code-editor/ace.js" type="text/javascript" charset="utf-8"></script>
-  <!-- Disabled in favour of dynamic: script src="/static/js/ace-code-editor/mode-sql.js" type="text/javascript" charset="utf-8" -->
-  <script src="/dynamic/mode-sql.js" type="text/javascript" charset="utf-8"></script>
-  <script src="/static/js/ace-code-editor/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
-  <script src="/static/js/ace-code-editor/theme-sqlserver.js" type="text/javascript" charset="utf-8"></script>
-  <script src="/static/js/ace-code-editor/snippets/sql.js" type="text/javascript" charset="utf-8"></script>
-  <script src="/static/js/ace-code-editor/mode-snippets.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/jquery.form.js"></script>
+<script src="/static/js/querySubmission.js"></script>
+<!-- Ace Libraries for Syntax Formatting -->
+<script src="/static/js/ace-code-editor/ace.js" type="text/javascript" charset="utf-8"></script>
+<!-- Disabled in favour of dynamic: script src="/static/js/ace-code-editor/mode-sql.js" type="text/javascript" charset="utf-8" -->
+<script src="/dynamic/mode-sql.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/ace-code-editor/ext-language_tools.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/ace-code-editor/theme-sqlserver.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/ace-code-editor/snippets/sql.js" type="text/javascript" charset="utf-8"></script>
+<script src="/static/js/ace-code-editor/mode-snippets.js" type="text/javascript" charset="utf-8"></script>
 
 <script>
     var globalconfig = {
@@ -176,11 +174,14 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
               </label>
             </div>
             </div>
-            <button class="btn btn-default" type=<#if model.isOnlyImpersonationEnabled()>"button" onclick="doSubmitQueryWithUserName()"<#else>"submit"</#if>>
+            <button class="btn btn-default" type="button" onclick="<#if model.isOnlyImpersonationEnabled()>doSubmitQueryWithUserName()<#else>submitQuery()</#if>">
             Re-run query
             </button>
           </form>
       </p>
+
+<#include "*/runningQuery.ftl">
+
       <p>
       <form action="/profiles/cancel/${model.queryId}" method="GET">
         <div class="form-group">
@@ -573,7 +574,8 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
     document.getElementById('queryForm')
             .addEventListener('keydown', function(e) {
       if (!(e.keyCode == 13 && (e.metaKey || e.ctrlKey))) return;
-      if (e.target.form) doSubmitQueryWithUserName();
+      if (e.target.form) 
+        <#if model.isOnlyImpersonationEnabled()>doSubmitQueryWithUserName()<#else>submitQuery()</#if>;
     });
     </script>
 
diff --git a/exec/java-exec/src/main/resources/rest/query/query.ftl b/exec/java-exec/src/main/resources/rest/query/query.ftl
index fd3e8bd6dd3..c4549f7a51a 100644
--- a/exec/java-exec/src/main/resources/rest/query/query.ftl
+++ b/exec/java-exec/src/main/resources/rest/query/query.ftl
@@ -19,10 +19,8 @@
 -->
 <#include "*/generic.ftl">
 <#macro page_head>
-    <#if model?? && model>
-      <script src="/static/js/jquery.form.js"></script>
-      <script src="/static/js/querySubmission.js"></script>
-    </#if>
+  <script src="/static/js/jquery.form.js"></script>
+  <script src="/static/js/querySubmission.js"></script>
   <!-- Ace Libraries for Syntax Formatting -->
   <script src="/static/js/ace-code-editor/ace.js" type="text/javascript" charset="utf-8"></script>
   <!-- Disabled in favour of dynamic: script src="/static/js/ace-code-editor/mode-sql.js" type="text/javascript" charset="utf-8" -->
@@ -41,7 +39,9 @@
     Sample SQL query: <strong>SELECT * FROM cp.`employee.json` LIMIT 20</strong>
   </div>
 
-  <#if model?? && model>
+<#include "*/runningQuery.ftl">
+
+  <#if model.isOnlyImpersonationEnabled()>
      <div class="form-group">
        <label for="userName">User Name</label>
        <input type="text" size="30" name="userName" id="userName" placeholder="User Name">
@@ -77,9 +77,10 @@
       <input class="form-control" type="hidden" id="query" name="query"/>
     </div>
 
-    <button class="btn btn-default" type=<#if model?? && model>"button" onclick="doSubmitQueryWithUserName()"<#else>"submit"</#if>>
+    <button class="btn btn-default" type="button" onclick="<#if model.isOnlyImpersonationEnabled()>doSubmitQueryWithUserName()<#else>wrapAndSubmitQuery()</#if>">
       Submit
     </button>
+    <input type="checkbox" name="forceLimit" value="limit" <#if model.isAutoLimitEnabled()>checked</#if>> Limit results to <input type="text" id="queryLimit" min="0" value="${model.getDefaultRowsAutoLimited()}" size="6" pattern="[0-9]*"> rows <span class="glyphicon glyphicon-info-sign" onclick="alert('Limits the number of records retrieved in the query')" style="cursor:pointer"></span>
   </form>
 
   <script>
@@ -125,10 +126,10 @@
     document.getElementById('queryForm')
             .addEventListener('keydown', function(e) {
       if (!(e.keyCode == 13 && (e.metaKey || e.ctrlKey))) return;
-      if (e.target.form) doSubmitQueryWithUserName();
+      if (e.target.form) //Submit [Wrapped] Query 
+        <#if model.isOnlyImpersonationEnabled()>doSubmitQueryWithUserName()<#else>wrapAndSubmitQuery()</#if>;
     });
   </script>
-
 </#macro>
 
 <@page_html/>
diff --git a/exec/java-exec/src/main/resources/rest/query/result.ftl b/exec/java-exec/src/main/resources/rest/query/result.ftl
index 7a17c648b8d..9f16c1f3150 100644
--- a/exec/java-exec/src/main/resources/rest/query/result.ftl
+++ b/exec/java-exec/src/main/resources/rest/query/result.ftl
@@ -74,7 +74,7 @@
       $('#result').dataTable( {
         "aaSorting": [],
         "scrollX" : true,
-        "lengthMenu": [[10, 25, 50, 100, -1], [10, 25, 50, 100, "All"]],
+        "lengthMenu": [[${model.getRowsPerPageValues()},-1], [${model.getRowsPerPageValues()},"ALL"]],
         "lengthChange": true,
         "dom": '<"H"lCfr>t<"F"ip>',
         "jQueryUI" : true
diff --git a/exec/java-exec/src/main/resources/rest/runningQuery.ftl b/exec/java-exec/src/main/resources/rest/runningQuery.ftl
new file mode 100644
index 00000000000..7978cf5951b
--- /dev/null
+++ b/exec/java-exec/src/main/resources/rest/runningQuery.ftl
@@ -0,0 +1,44 @@
+<#--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+
+-->
+  <!-- Loading Modal -->
+  <div class="modal fade" id="queryLoadingModal" role="dialog"  data-backdrop="static" data-keyboard="false">
+    <div class="modal-dialog">
+      <!-- Modal content-->
+      <div class="modal-content">
+        <div class="modal-header">
+          <h4 class="modal-title" id="cancelTitle">Query Submitted</h4>
+        </div>
+        <div class="modal-body" style="line-height:3">
+            <table border="0px" width="100%"><tr>
+                <td align="center" style="font-size:125%">Waiting for results... (This may take some time) <br>Please don't close this window</td>
+                <td align="right"><img src="/static/img/loader.gif"></td>
+            </tr></table>
+        </div>
+        <div class="modal-footer">
+        <table border="0px" width="100%"><tr>
+        <td align="left" id="stopWatch">Elapsed Time: 00:00</td><td align="right">
+          <button type="button" class="btn btn-default" title="Check profiles in new tab" onclick="window.open(&#39;/profiles&#39;)">Check Status <span class="glyphicon glyphicon-new-window"></span></button>
+          </td>
+          </tr>
+          </table>
+        </div>
+      </div>
+    </div>
+  </div>
\ No newline at end of file
diff --git a/exec/java-exec/src/main/resources/rest/static/img/loader.gif b/exec/java-exec/src/main/resources/rest/static/img/loader.gif
new file mode 100644
index 00000000000..0b0294bc2bc
Binary files /dev/null and b/exec/java-exec/src/main/resources/rest/static/img/loader.gif differ
diff --git a/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js b/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js
index 638ddbfab91..1183682a76a 100644
--- a/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js
+++ b/exec/java-exec/src/main/resources/rest/static/js/querySubmission.js
@@ -10,27 +10,98 @@
  *  OF ANY KIND, either express or implied. See the License for the specific
  *  language governing permissions and limitations under the License.
  */
+var userName = null;
+//Elements for Timer in LoadingModal
+var elapsedTime = 0;
+var delay = 1000; //msec
+var timeTracker = null; //Handle for stopping watch
+var userName = null;
 
+//Show cancellation status
+function popupAndWait() {
+  elapsedTime=0; //Init
+  $("#queryLoadingModal").modal("show");
+  var stopWatchElem = $('#stopWatch'); //Get handle on time progress elem within Modal
+  //Timer updating
+  timeTracker = setInterval(function() {
+    elapsedTime = elapsedTime + delay/1000;
+    let time = elapsedTime;
+    let minutes = Math.floor(time / 60);
+    let seconds = time - minutes * 60;
+    let prettyTime = ("0" + minutes).slice(-2)+':'+ ("0" + seconds).slice(-2);
+    stopWatchElem.text('Elapsed Time : ' + prettyTime);
+  }, delay);
+}
+
+//Close the cancellation status popup
+function closePopup() {
+  clearInterval(timeTracker);
+  $("#queryLoadingModal").modal("hide");
+}
+
+// Wrap & Submit Query (invoked if impersonation is enabled to check for username)
 function doSubmitQueryWithUserName() {
-    var userName = document.getElementById("userName").value;
+    userName = document.getElementById("userName").value;
     if (!userName.trim()) {
         alert("Please fill in User Name field");
         return;
     }
+    //Wrap and Submit query
+    wrapAndSubmitQuery();
+}
+
+//Wrap & Submit Query (invoked directly if impersonation is not enabled)
+function wrapAndSubmitQuery() {
+    //Wrap if required
+    wrapQuery();
+    //Submit query
+    submitQuery();
+}
+
+//Submit Query
+function submitQuery() {
+    popupAndWait();
+    //Submit query
     $.ajax({
         type: "POST",
         beforeSend: function (request) {
-            request.setRequestHeader("User-Name", userName);
+            if (typeof userName !== 'undefined' && userName !== null && userName.length > 0) {
+              request.setRequestHeader("User-Name", userName);
+            }
         },
         url: "/query",
         data: $("#queryForm").serializeArray(),
         success: function (response) {
+            closePopup();
             var newDoc = document.open("text/html", "replace");
             newDoc.write(response);
             newDoc.close();
         },
         error: function (request, textStatus, errorThrown) {
+            closePopup();
             alert(errorThrown);
         }
     });
-}
\ No newline at end of file
+}
+
+//Wraps a query with Limit by directly changing the query in the hidden textbox in the UI (see /query.ftl)
+function wrapQuery() {
+    var origQueryText = $('#query').attr('value');
+    //dBug: console.log("Query Input:" + origQueryText);
+    var mustWrapWithLimit = $('input[name="forceLimit"]:checked').length > 0;
+    if (mustWrapWithLimit) {
+        var semicolonIdx = origQueryText.lastIndexOf(';');
+        //Check and eliminate trailing semicolon
+        if (semicolonIdx  == origQueryText.length-1 ) {
+          origQueryText = origQueryText.substring(0, semicolonIdx)
+        }
+        var qLimit = $('#queryLimit').val();
+        var wrappedQuery = "-- [autoLimit: " + qLimit + " rows]\nselect * from (\n" + origQueryText + "\n) limit " + qLimit;
+        //dBug: console.log("Query Output:" + wrappedQuery);
+        //Wrapping Query
+        $('#query').attr('value', wrappedQuery);
+    } else {
+        //Do not change the query
+        //dBug: console.log("Query Output:" + origQueryText);
+    }
+}


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
users@infra.apache.org


With regards,
Apache Git Services