blob: efb1c15c296bdc72ac4465c9f874f43d73fb0028 [file] [log] [blame]
/**
* @@@ START COPYRIGHT @@@
*
* (C) Copyright 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed 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.
*
* @@@ END COPYRIGHT @@@
*/
var setRootNode = false;
var LOADING_SELECTOR = ".hpdsm-spinner";
var st = null;
var resizeTimer = null;
var ROUTE_FILTER="/queryplan/filter";
var formDialogContents;
var formStateView;
var HPDSMDATATABLE = "#query-result-container";
var GRIDCONTAINER = "#hpdsm-1";
var ohpdsmTable = null;
var controlStatements = null;
var previousScrollTop = 0;
var controlDialog = null;
var controlStmts = "";
var deselectedButtonStyle =
{
"color": "#007dba",
"border": "2px solid #BFE5F5",
"border-top-right-radius": "5px",
"border-bottom-left-radius": "5px",
"background-color": "#BFE5F5"
};
var selectedButtonStyle =
{
"background-color": "#0096d6",
"border-color": "#0096d6",
"color": "#fff"
};
//Implement a node rendering function called 'nodeline' that plots a straight line
//when contracting or expanding a subtree.
$jit.ST.Plot.NodeTypes.implement({
'nodeline': {
'render': function(node, canvas, animating) {
if(animating === 'expand' || animating === 'contract') {
var pos = node.pos.getc(true), nconfig = this.node, data = node.data;
var width = nconfig.width, height = nconfig.height;
var algnPos = this.getAlignedPos(pos, width, height);
var ctx = canvas.getCtx(), ort = this.config.orientation;
ctx.beginPath();
if(ort == 'left' || ort == 'right') {
ctx.moveTo(algnPos.x, algnPos.y + height / 2);
ctx.lineTo(algnPos.x + width, algnPos.y + height / 2);
} else {
ctx.moveTo(algnPos.x + width / 2, algnPos.y);
ctx.lineTo(algnPos.x + width / 2, algnPos.y + height);
}
ctx.stroke();
}
}
}
});
$( document ).ready(function() {
init();
});
function showLoading(){
$('#loadingImg').show();
};
function hideLoading() {
$('#loadingImg').hide();
};
function drawExplain(jsonData) {
hideLoading();
$("#infovis").empty();
//init Spacetree
//Create a new ST instance
st = new $jit.ST({
'injectInto': 'infovis',
orientation: "top",
constrained: false,
//set duration for the animation
duration: 800,
//set animation transition type
transition: $jit.Trans.Quart.easeInOut,
//set distance between node and its children
levelDistance: 40,
siblingOffset: 100,
width: ($(GRIDCONTAINER).width()),
height:($(GRIDCONTAINER).height()),
//set max levels to show. Useful when used with
//the request method for requesting trees of specific depth
levelsToShow: 20,
offsetX: -100,
offsetY: 350,
//set node and edge styles
//set overridable=true for styling individual
//nodes or edges
Node: {
height: 20,
width: 40,
//use a custom
//node rendering function
type: 'nodeline',
color:'#23A4FF',
lineWidth: 2,
align:"center",
overridable: true
},
Navigation: {
enable: true,
//panning: 'avoid nodes',
panning: true,
zooming: 20
},
Edge: {
type: 'bezier',
lineWidth: 2,
color:'#23A4FF',
overridable: true
},
Tips: {
enable: true,
type: 'auto',
offsetX: 20,
offsetY: 20,
onShow: function(tip, node) {
tip.innerHTML = node.data.formattedCostDesc;
tip.style.width = 450 + 'px';
tip.className = 'mytooltip';
}
},
//Add a request method for requesting on-demand json trees.
//This method gets called when a node
//is clicked and its subtree has a smaller depth
//than the one specified by the levelsToShow parameter.
//In that case a subtree is requested and is added to the dataset.
//This method is asynchronous, so you can make an Ajax request for that
//subtree and then handle it to the onComplete callback.
//Here we just use a client-side tree generator (the getTree function).
request: function(nodeId, level, onComplete) {
//var ans = getTree(nodeId, level);
//onComplete.onComplete(nodeId, ans);
},
onBeforeCompute: function(node){
//Log.write("loading " + node.name);
},
onAfterCompute: function(){
//Log.write("done");
},
onComplete: function(){
if(!setRootNode){
var m = {
offsetX: st.canvas.translateOffsetX,
offsetY: st.canvas.translateOffsetY
};
st.onClick(st.root, { Move: m});
setRootNode = true;
}
},
//This method is called on DOM label creation.
//Use this method to add event handlers and styles to
//your node.
onCreateLabel: function(label, node){
var nodeName = node.name;
nodeName = nodeName.replace("_", " ");
nodeName = nodeName.replace("SEABASE","TRAFODION");
nodeName = toProperCase(nodeName);
var html = nodeName;
switch(node.name)
{
case 'FILE_SCAN':
case 'INDEX_SCAN':
case 'FILE_SCAN_UNIQUE':
case 'INDEX_SCAN_UNIQUE':
html = "<img src='img/file_scan.gif' />" + nodeName;
break;
case 'PARTITION_ACCESS':
html = "<img src='img/partition_scan.gif' />" + nodeName;
break;
case 'HASH_GROUPBY':
html = "<img src='img/hash_groupby.png' />" + nodeName;
break;
case 'HASH_PARTIAL_GROUPBY_LEAF':
nodeName = "Hash Groupby Leaf";
html = "<img src='img/hash_groupby.png' />" + nodeName;
break;
case 'HASH_PARTIAL_GROUPBY_ROOT':
nodeName = "Hash Groupby Root";
html = "<img src='img/hash_groupby.png' />" + nodeName;
break;
case 'SHORTCUT_SCALAR_AGRR':
case 'SORT_SCALAR_AGGR':
nodeName = "Scalar Aggr";
html = "<img src='img/scalar_aggr.png' />" + nodeName;
break;
case 'SORT':
case 'SORT_GROUPBY':
html = "<img src='img/sort_group_by.png' />" + nodeName;
break;
case 'SORT_PARTIAL_AGGR_LEAF':
nodeName = "Sort Aggr Leaf";
html = "<img src='img/sort_group_by.png' />" + nodeName;
break;
case 'SORT_PARTIAL_AGGR_ROOT':
nodeName = "Sort Aggr Root";
html = "<img src='img/sort_group_by.png' />" + nodeName;
break;
case 'SORT_PARTIAL_GROUPBY_LEAF':
nodeName = "Sort Groupby Leaf";
html = "<img src='img/sort_group_by.png' />" + nodeName;
break;
case 'SORT_PARTIAL_GROUPBY_ROOT':
nodeName = "Sort Groupby Root";
html = "<img src='img/sort_group_by.png' />" + nodeName;
break;
case 'INSERT':
case 'INSERT_VSBB':
html = "<img src='img/insert.gif' />" + nodeName;
break;
case 'PROBE_CACHE':
html = "<img src='img/probe_cache.png' />" + nodeName;
break;
case 'HYBRID_HASH_ANTI_SEMI_JOIN':
case 'HYBRID_HASH_JOIN':
case 'HYBRID_HASH_SEMI_JOIN':
nodeName = "Hash Join";
html = "<img src='img/hash_join.png' />" + nodeName;
break;
case 'LEFT_HYBRID_HASH_JOIN':
case 'LEFT_ORDERED_HASH_JOIN':
nodeName = "Left Hash Join";
html = "<img src='img/hash_join.png' />" + nodeName;
break;
case 'ORDERED_HASH_ANTI_SEMI_JOIN':
case 'ORDERED_HASH_JOIN':
case 'ORDERED_HASH_SEMI_JOIN':
nodeName = "Hash Join";
html = "<img src='img/hash_join.png' />" + nodeName;
break;
case 'TUPLE_FLOW':
html = "<img src='img/tuple_flow.png' />" + nodeName;
break;
case 'LEFT_MERGE_JOIN':
case 'MERGE_ANTI_SEMI_JOIN':
case 'MERGE_JOIN':
case 'MERGE_SEMI_JOIN':
html = "<img src='img/merge_join.gif' />" + nodeName;
break;
case 'NESTED_ANTI_SEMI_JOIN':
case 'LEFT_NESTED_JOIN':
case 'NESTED_JOIN':
case 'NESTED_SEMI_JOIN':
html = "<img src='img/nested_join.png' />" + nodeName;
break;
case 'MERGE_UNION':
html = "<img src='img/merge_union.gif' />" + nodeName;
break;
case 'ESP_EXCHANGE':
html = "<img src='img/esp_exchange.png' />" + nodeName;
break;
case 'SPLIT_TOP':
html = "<img src='img/split_top.gif' />" + nodeName;
break;
case 'HIVE_INSERT':
case 'TRAFODION_':
case 'TRAFODION_DELETE':
case 'TRAFODION_INSERT':
html = "<img src='img/trafodion_insert.png' />" + nodeName;
break;
case 'HIVE_SCAN':
case 'TRAFODION_SCAN':
case 'SEABASE_SCAN':
html = "<img src='img/seabase_scan.gif' />" + nodeName;
break;
case 'ROOT':
html = "<img src='img/root.png'/> " + nodeName;
break;
default:
html = "<img src='img/undefined.gif' />" + nodeName;
break;
}
label.id = node.id;
label.innerHTML = html;
label.onclick = function(){
var m = {
offsetX: st.canvas.translateOffsetX,
offsetY: st.canvas.translateOffsetY
};
st.onClick(node.id, { Move: m });
};
//set label styles
var style = label.style;
style.width = 175 + 'px';
style.height = 17 + 'px';
style.cursor = 'pointer';
style.color = '#000';
style.display = 'inline-table';
//style.backgroundColor = '#1a1a1a';
style.fontSize = '15px';
style.fontWeight = 'bold';
//style.textAlign= 'center';
//style.textDecoration = 'underline';
//style.paddingTop = '3px';
style.paddingLeft = '3px';
},
//This method is called right before plotting
//a node. It's useful for changing an individual node
//style properties before plotting it.
//The data properties prefixed with a dollar
//sign will override the global node style properties.
onBeforePlotNode: function(node){
//add some color to the nodes in the path between the
//root node and the selected node.
if (node.selected) {
node.data.$color = "#ff7";
}
else {
delete node.data.$color;
}
},
//This method is called right before plotting
//an edge. It's useful for changing an individual edge
//style properties before plotting it.
//Edge data proprties prefixed with a dollar sign will
//override the Edge global style properties.
onBeforePlotLine: function(adj){
if (adj.nodeFrom.selected && adj.nodeTo.selected) {
adj.data.$color = "#eed";
adj.data.$lineWidth = 3;
}
else {
delete adj.data.$color;
delete adj.data.$lineWidth;
}
}
});
//load json data
st.loadJSON(jsonData);
//compute node positions and layout
st.compute();
$("#infovis").show();
//emulate a click on the root node.
st.onClick(st.root);
doResize();
//end
};
function toProperCase(s) {
return s.toLowerCase().replace(/^(.)|\s(.)/g, function($1) {
return $1.toUpperCase();
});
};
function init() {
controlDialog = $("#dialog-form").dialog({
autoOpen: false,
height: 300,
width: 500,
modal: true,
buttons: {
"OK": function (){
controlStmts = $("#controlStmts").val();
if(controlStmts == null) {
controlStmts = "";
} else {
controlStmts = controlStmts.replace(/(\r\n|\n|\r)/gm,"");
}
controlDialog.dialog( "close" );
},
Cancel: function() {
controlDialog.dialog( "close" );
}
},
close: function() {
//form[ 0 ].reset();
//allFields.removeClass( "ui-state-error" );
}
});
hideLoading();
//initFilterDialog();
$("#explainQuery").on('click', explainQuery);
$("#executeQuery").on('click', executeQuery);
$("#setControlStmts").on('click', openFilterDialog);
$("#infovis").show();
$("#errorText").hide();
};
function onRelayout() {
onResize();
};
function onResize() {
clearTimeout(resizeTimer);
resizeTimer = setTimeout(doResize, 200);
};
function doResize () {
if(st != null) {
st.canvas.resize($('#infovis').width(), ($(GRIDCONTAINER).height() + $(GRIDCONTAINER).scrollTop() + 800));
}
};
function openFilterDialog () {
controlDialog.dialog( "open" );
};
function explainQuery() {
showLoading();
$("#infovis").hide();
$("#errorText").hide();
$("#query-result-container").hide();
var queryText = $("#query-text").val();
var param = {sQuery : queryText, sControlStmts: controlStmts};
$.ajax({
url:'queryPlan.jsp',
type:'POST',
data: JSON.stringify(param),
dataType:"json",
contentType: "application/json;",
success:drawExplain,
error:function(jqXHR, res, error){
hideLoading();
showErrorMessage(jqXHR);
}
});
};
function executeQuery() {
showLoading();
$("#infovis").hide();
$("#errorText").hide();
$("#query-result-container").show();
var queryText = $("#query-text").val();
var param = {sQuery : queryText, sControlStmts: controlStmts};
$.ajax({
url:'executeQuery.jsp',
type:'POST',
data: JSON.stringify(param),
dataType:"json",
contentType: "application/json;",
success:displayResults,
error:function(jqXHR, res, error){
hideLoading();
showErrorMessage(jqXHR);
}
});
};
function displayResults(result){
hideLoading();
var keys;
$.each(result, function(i, data){
keys = Object.keys(data);
});
if(keys != null && keys.length > 0) {
var sb = '<table class="display1" id="query-results" style="font-size:small"><thead><tr>';
for (var r=0,len=keys.length; r<len; r++) {
sb += '<th><b></b>' + keys[r] + '</th>';
}
sb += '</tr></thead><tbody> </tbody></table>';
$('#query-result-container').html( sb );
var aoColumns = [];
var aaData = [];
$.each(result, function(i, data){
var rowData = [];
$.each(keys, function(k, v) {
rowData.push(data[v]);
});
aaData.push(rowData);
});
// add needed columns
$.each(keys, function(k, v) {
obj = new Object();
obj.sTitle = v;
aoColumns.push(obj);
});
var bPaging = aaData.length > 25;
$('#query-results').dataTable({
"bProcessing": true,
"bJQueryUI": true,
"bPaginate" : bPaging,
"iDisplayLength" : 25,
"sPaginationType": "full_numbers",
"aaData": aaData,
"aoColumns" : aoColumns
});
}
};
function showErrorMessage (jqXHR) {
hideLoading();
$("#infovis").hide();
$("#query-result-container").hide();
$("#errorText").show();
if (jqXHR.responseText) {
$("#errorText").text(jqXHR.responseText);
}
};
//@ sourceURL=ExplainPlanView.js