diff --git a/pkg/api/api.go b/pkg/api/api.go index 6c07e88..0169c2f 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -280,7 +280,7 @@ func RunQuery(c *gin.Context) { HandleQuery(query, c) } -// ExplainQuery renders query analyze profile +// ExplainQuery renders query explain plan func ExplainQuery(c *gin.Context) { query := cleanQuery(c.Request.FormValue("query")) @@ -289,6 +289,18 @@ func ExplainQuery(c *gin.Context) { return } + HandleQuery(fmt.Sprintf("EXPLAIN %s", query), c) +} + +// AnalyzeQuery renders query explain plan and analyze profile +func AnalyzeQuery(c *gin.Context) { + query := cleanQuery(c.Request.FormValue("query")) + + if query == "" { + badRequest(c, errQueryRequired) + return + } + HandleQuery(fmt.Sprintf("EXPLAIN ANALYZE %s", query), c) } diff --git a/pkg/api/routes.go b/pkg/api/routes.go index c2b1fbf..3f94f5f 100644 --- a/pkg/api/routes.go +++ b/pkg/api/routes.go @@ -50,6 +50,8 @@ func SetupRoutes(router *gin.Engine) { api.POST("/query", RunQuery) api.GET("/explain", ExplainQuery) api.POST("/explain", ExplainQuery) + api.GET("/analyze", AnalyzeQuery) + api.POST("/analyze", AnalyzeQuery) api.GET("/history", GetHistory) api.GET("/bookmarks", GetBookmarks) api.GET("/export", DataExport) diff --git a/static/css/app.css b/static/css/app.css index 0ac0f76..4041dc6 100644 --- a/static/css/app.css +++ b/static/css/app.css @@ -284,7 +284,6 @@ #input { height: 255px; - overflow: hidden; } #input .wrapper { @@ -571,6 +570,10 @@ display: block; } +.left { + float: left; +} + /* -------------------------------------------------------------------------- */ #custom_query { diff --git a/static/index.html b/static/index.html index da03ec4..66dff08 100644 --- a/static/index.html +++ b/static/index.html @@ -14,6 +14,7 @@ + @@ -76,7 +77,15 @@
- +
+ + +
Please wait, query is executing...
diff --git a/static/js/app.js b/static/js/app.js index 9a26b36..abc0833 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -90,6 +90,7 @@ function getHistory(cb) { apiCall("get", "/history", {}, cb) function getBookmarks(cb) { apiCall("get", "/bookmarks", {}, cb); } function executeQuery(query, cb) { apiCall("post", "/query", { query: query }, cb); } function explainQuery(query, cb) { apiCall("post", "/explain", { query: query }, cb); } +function analyzeQuery(query, cb) { apiCall("post", "/analyze", { query: query }, cb); } function disconnect(cb) { apiCall("post", "/disconnect", {}, cb); } function encodeQuery(query) { @@ -607,25 +608,33 @@ function showActivityPanel() { }); } +function showQueryProgressMessage() { + $("#run, #explain-dropdown-toggle, #csv, #json, #xml").prop("disabled", true); + $("#explain-dropdown").removeClass("open"); + $("#query_progress").show(); +} + +function hideQueryProgressMessage() { + $("#run, #explain-dropdown-toggle, #csv, #json, #xml").prop("disabled", false); + $("#query_progress").hide(); +} + function runQuery() { setCurrentTab("table_query"); - $("#run, #explain, #csv, #json, #xml").prop("disabled", true); - $("#query_progress").show(); + showQueryProgressMessage(); var query = $.trim(editor.getSelectedText() || editor.getValue()); if (query.length == 0) { - $("#run, #explain, #csv, #json, #xml").prop("disabled", false); - $("#query_progress").hide(); + hideQueryProgressMessage(); return; } executeQuery(query, function(data) { buildTable(data); - $("#run, #explain, #csv, #json, #xml").prop("disabled", false); - $("#query_progress").hide(); + hideQueryProgressMessage(); $("#input").show(); $("#body").removeClass("full"); $("#results").data("mode", "query"); @@ -644,22 +653,41 @@ function runQuery() { function runExplain() { setCurrentTab("table_query"); - $("#run, #explain, #csv, #json, #xml").prop("disabled", true); - $("#query_progress").show(); + showQueryProgressMessage(); var query = $.trim(editor.getSelectedText() || editor.getValue()); if (query.length == 0) { - $("#run, #explain, #csv, #json, #xml").prop("disabled", false); - $("#query_progress").hide(); + hideQueryProgressMessage(); return; } explainQuery(query, function(data) { buildTable(data); - $("#run, #explain, #csv, #json, #xml").prop("disabled", false); - $("#query_progress").hide(); + hideQueryProgressMessage(); + $("#input").show(); + $("#body").removeClass("full"); + $("#results").addClass("no-crop"); + }); +} + +function runAnalyze() { + setCurrentTab("table_query"); + + showQueryProgressMessage(); + + var query = $.trim(editor.getSelectedText() || editor.getValue()); + + if (query.length == 0) { + hideQueryProgressMessage(); + return; + } + + analyzeQuery(query, function(data) { + buildTable(data); + + hideQueryProgressMessage(); $("#input").show(); $("#body").removeClass("full"); $("#results").addClass("no-crop"); @@ -1115,6 +1143,10 @@ $(document).ready(function() { runExplain(); }); + $("#analyze").on("click", function() { + runAnalyze(); + }); + $("#csv").on("click", function() { exportTo("csv"); }); diff --git a/static/js/bootstrap-dropdown.js b/static/js/bootstrap-dropdown.js new file mode 100644 index 0000000..6881cd2 --- /dev/null +++ b/static/js/bootstrap-dropdown.js @@ -0,0 +1,150 @@ +/* ======================================================================== + * Bootstrap: dropdown.js v3.2.0 + * http://getbootstrap.com/javascript/#dropdowns + * ======================================================================== + * Copyright 2011-2014 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + * ======================================================================== */ + ++function ($) { + 'use strict'; + + // DROPDOWN CLASS DEFINITION + // ========================= + + var backdrop = '.dropdown-backdrop' + var toggle = '[data-toggle="dropdown"]' + var Dropdown = function (element) { + $(element).on('click.bs.dropdown', this.toggle) + } + + Dropdown.VERSION = '3.2.0' + + Dropdown.prototype.toggle = function (e) { + var $this = $(this) + + if ($this.is('.disabled, :disabled')) return + + var $parent = getParent($this) + var isActive = $parent.hasClass('open') + + clearMenus() + + if (!isActive) { + if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { + // if mobile we use a backdrop because click events don't delegate + $('