ASTERIXDB-1083: Fixed non-query statements' plans display on WebUI
Change-Id: I52dd69062b2aaf89798ebb8e0e58a1941ac4119e
Reviewed-on: https://asterix-gerrit.ics.uci.edu/390
Reviewed-by: Chris Hillery <ceej@lambda.nu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index 0031e0d..91d8358 100644
--- a/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -23,8 +23,6 @@
import java.util.ArrayList;
import java.util.List;
-import org.json.JSONException;
-
import org.apache.asterix.api.common.Job.SubmissionMode;
import org.apache.asterix.aql.base.Statement.Kind;
import org.apache.asterix.aql.expression.FunctionDecl;
@@ -78,12 +76,14 @@
import org.apache.hyracks.api.client.IHyracksClientConnection;
import org.apache.hyracks.api.job.JobId;
import org.apache.hyracks.api.job.JobSpecification;
+import org.json.JSONException;
/**
* Provides helper methods for compilation of a query into a JobSpec and submission
* to Hyracks through the Hyracks client interface.
*/
public class APIFramework {
+ public static final String HTML_STATEMENT_SEPARATOR = "<!-- BEGIN -->";
private static List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> buildDefaultLogicalRewrites() {
List<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>> defaultLogicalRewrites = new ArrayList<Pair<AbstractRuleController, List<IAlgebraicRewriteRule>>>();
diff --git a/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java b/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
index 39a5644..35d4c37 100644
--- a/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
+++ b/asterix-app/src/main/java/org/apache/asterix/api/http/servlet/APIServlet.java
@@ -34,6 +34,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.asterix.api.common.APIFramework;
import org.apache.asterix.api.common.SessionConfig;
import org.apache.asterix.api.common.SessionConfig.OutputFormat;
import org.apache.asterix.aql.base.Statement;
@@ -63,15 +64,12 @@
String output = request.getParameter("output-format");
if (output.equals("ADM")) {
format = OutputFormat.ADM;
- }
- else if (output.equals("CSV")) {
+ } else if (output.equals("CSV")) {
format = OutputFormat.CSV;
- }
- else if (output.equals("CSV-Header")) {
+ } else if (output.equals("CSV-Header")) {
format = OutputFormat.CSV;
csv_and_header = true;
- }
- else {
+ } else {
// Default output format
format = OutputFormat.JSON;
}
@@ -106,8 +104,7 @@
sessionConfig.set(SessionConfig.FORMAT_HTML, true);
sessionConfig.set(SessionConfig.FORMAT_CSV_HEADER, csv_and_header);
sessionConfig.setOOBData(isSet(printExprParam), isSet(printRewrittenExprParam),
- isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam),
- isSet(printJob));
+ isSet(printLogicalPlanParam), isSet(printOptimizedLogicalPlanParam), isSet(printJob));
MetadataManager.INSTANCE.init();
AqlTranslator aqlTranslator = new AqlTranslator(aqlStatements, sessionConfig);
double duration = 0;
@@ -115,6 +112,7 @@
aqlTranslator.compileAndExecute(hcc, hds, AqlTranslator.ResultDelivery.SYNC);
long endTime = System.currentTimeMillis();
duration = (endTime - startTime) / 1000.00;
+ out.println(APIFramework.HTML_STATEMENT_SEPARATOR);
out.println("<PRE>Duration of all jobs: " + duration + " sec</PRE>");
} catch (ParseException | TokenMgrError | org.apache.asterix.aqlplus.parser.TokenMgrError pe) {
GlobalConfig.ASTERIX_LOGGER.log(Level.INFO, pe.toString(), pe);
diff --git a/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java b/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
index a684cac..dcfbc98 100644
--- a/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
+++ b/asterix-app/src/main/java/org/apache/asterix/aql/translator/AqlTranslator.java
@@ -259,6 +259,9 @@
Map<String, String> config = new HashMap<String, String>();
for (Statement stmt : aqlStatements) {
+ if (sessionConfig.is(SessionConfig.FORMAT_HTML)) {
+ sessionConfig.out().println(APIFramework.HTML_STATEMENT_SEPARATOR);
+ }
validateOperation(activeDefaultDataverse, stmt);
AqlMetadataProvider metadataProvider = new AqlMetadataProvider(activeDefaultDataverse,
CentralFeedManager.getInstance());
diff --git a/asterix-app/src/main/resources/webui/querytemplate.html b/asterix-app/src/main/resources/webui/querytemplate.html
index ee49122..9088251 100644
--- a/asterix-app/src/main/resources/webui/querytemplate.html
+++ b/asterix-app/src/main/resources/webui/querytemplate.html
@@ -16,13 +16,41 @@
! specific language governing permissions and limitations
! under the License.
!-->
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta name="description" content="ASTERIX WEB PAGE" />
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<link
+ href='http://fonts.googleapis.com/css?family=Bitter|PT+Sans+Caption|Open+Sans'
+ rel='stylesheet' type='text/css'>
+<script src="/webui/static/js/jquery.min.js"></script>
+
+<link href="/webui/static/css/bootstrap.min.css" rel="stylesheet"
+ type="text/css" />
+<link href="/webui/static/css/bootstrap-responsive.min.css"
+ rel="stylesheet" type="text/css" />
+
+<script src="/webui/static/js/bootstrap.min.js"></script>
+
+<link href="/webui/static/css/style.css" rel="stylesheet"
+ type="text/css" />
+
+<script type="text/javascript">
+$(document).ready(function() {
+
+ var optionButtonSize = $('#checkboxes-on').width();
+ $('#clear-query-button, #run-btn').width(optionButtonSize);
+
+ $('#checkboxes-on').click(function() {
+ /* Displays a checkmark to indicate selection/clearing */
if ($('#opts').is(":visible")) {
$('#opts').hide();
$('#queryform :input').prop('checked', false);
} else {
- $('#opts').show();
+ $('#opts').show();
$('#queryform :input').prop('checked', true);
- }
+ }
return false;
});
@@ -31,7 +59,7 @@
return false;
});
- $('form#queryform :input').click( function () {
+ $('form#queryform :input').click(function() {
/* Hides selection check on uncheck, shows when all 5 selected */
if ($(this).val()) {
if ($(this).is(':checked') && $('input[type=checkbox]').filter(':checked').length == 5) {
@@ -45,74 +73,90 @@
$("form#queryform").submit(function() {
$('#output-message').html("");
$.post("/", $("form#queryform").serialize(), function(data) {
-
- var resSet = 0;
- var resPattern = /<h4>Results:<\/h4>/g;
- var durPattern = /<PRE>Duration/g;
+ var durPattern = '<PRE>Duration';
var errorPattern = /<div class="accordion" id="errorblock">/g;
- var resultCount = data.match(resPattern);
-
- if (!resPattern.test(data)) {
- if(errorPattern.test(data)) {
- $('#output-heading').html('Error');
- $('#output-heading').addClass('error');
- } else {
- $('#output-heading').html('Output');
- $('#output-heading').removeClass('error');
- }
- $('#output-message').html(data);
+ var sectionsSeparator = '<h4>';
+ var resultPat = 'Results:</h4>';
+
+ if (errorPattern.test(data)) {
+ $('#output-heading').html('Error');
+ $('#output-heading').addClass('error');
} else {
$('#output-heading').html('Output');
$('#output-heading').removeClass('error');
- if (resultCount.length <= 1) {
- $('#output-message').html(data);
- } else {
- var splitData = data.split('<PRE>Duration');
- var results = splitData[0].split('<h4>');
- var components = results.slice(1, results.length);
- var sections = components.length / resultCount.length;
+ }
+ var executedStatements = data.split('<!-- BEGIN -->');
+ var executedStatementsWithResultsCount = 0;
+ for (var i = 0; i < executedStatements.length; i++) {
+ if (executedStatements[i].toString().trim().length > 0) {
+ /* check how many statements have returned data*/
+ executedStatementsWithResultsCount++;
+ }
+ }
- for (resSet = 0; resSet < resultCount.length; resSet++) {
+ /* only a single statement returned results and/or duration message*/
+ if (executedStatementsWithResultsCount <= 2) {
+ /* print statement results and duration*/
+ $('#output-message').html(data);
+ } else {
+ var resultsCount = 1;
+ /* need to create collapse button and div per statement*/
+ for (var i = 0; i < executedStatements.length; i++) {
+ /* last statement is always the duration message*/
+ if (i == (executedStatements.length - 1)) {
+ /* print duration message*/
+ $('#output-message').append(executedStatements[i]);
+ break;
+ }
- $('#output-message').append('<h4>' + components[(resSet+1)*sections - 1]);
+ if (executedStatements[i].toString().trim().length > 0) {
+ var sections = executedStatements[i].toString().split(sectionsSeparator);
+ /* remove the first section since it is always empty due to splitng on sectionsSeparator */
+ sections.splice(0, 1);
- if (sections > 1) {
- var resNum = resSet + 1;
+ /* if there is a results section, we need to put it before the collapsible section*/
+ for (var j = 0; j < sections.length; j++) {
+ /* print results section and remove it*/
+ if (sections[j].indexOf(resultPat) >= 0) {
+ var resultsSection = sections.splice(j, 1);
+ $('#output-message').append(sectionsSeparator + resultsSection.toString());
+ }
+ }
+
+ if (sections.length > 0) {
+ /* generate the collapsible section for this statement*/
$('<button/>')
.attr("class", "btn")
.attr("data-toggle", "collapse")
- .attr("data-target", "#collapse" + resSet)
+ .attr("data-target", "#collapse" + i)
.css("margin-bottom", "1em")
- .html('Result Plan #' + resNum + '<i id="ibtn' + resSet + '" class="icon-plus extarget"></i>')
+ .html('Result Plan #' + resultsCount + '<i id="ibtn' + resultsCount + '" class="icon-plus extarget"></i>')
.appendTo('#output-message');
$('<div/>')
- .attr("id", "collapse" + resSet)
+ .attr("id", "collapse" + i)
.attr("class", "collapse in")
.appendTo('#output-message');
- for (var c = 0; c < sections - 1; c++) {
- var pos = resSet*sections + c;
- $('#collapse' + resSet).append('<h4>' + components[pos]);
+ /* put the rest of the sections in the collapsible section*/
+ for (var k = 0; k < sections.length; k++) {
+ $('#collapse' + i).append(sectionsSeparator + sections[k].toString());
}
-
- /* Placeholder for future on show/hide result plan behavior
- $('#collapse' + resSet).on('show', function() {
- }).on('hide', function() {
- });
- */
-
- $('#output-message').append("<hr/>");
}
+ $('#output-message').append("<hr/>");
+ resultsCount++;
+ /* Placeholder for future on show/hide result plan behavior
+ $('#collapse' + resSet).on('show', function() {
+ }).on('hide', function() {
+ });
+ */
}
- $('#output-message').append('<PRE>Duration' + splitData[1]);
}
-
}
var contentString = data.toString();
- if (contentString.indexOf("<PRE>Duration") !== -1) {
+ if (contentString.indexOf(durPattern) != -1) {
$('<div/>')
.addClass("alert alert-success")
.html("Success: Query Complete")