Skip to content

Instantly share code, notes, and snippets.

@s3u
Created June 8, 2012 15:57
Show Gist options
  • Save s3u/2896381 to your computer and use it in GitHub Desktop.
Save s3u/2896381 to your computer and use it in GitHub Desktop.
Visualize the execution plan for ql.io scripts
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></script>
<script type="text/javascript" src="http://localhost:3000/scripts/jquery-min.js"></script>
<script type="text/javascript" src="http://localhost:3000/scripts/jquery-ui.min.js"></script>
<script type="text/javascript" src="http://ql.io/scripts/compiler.js"></script>
<script type="text/javascript" src="http://codemirror.net/lib/codemirror.js"></script>
<script type="text/javascript" src="http://ql.io/scripts/qlio-editor.js"></script>
<link rel="stylesheet" type="text/css" href="http://codemirror.net/lib/codemirror.css"/>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
width: 100%;
font-family: 'Droid Sans', 'Helvetica', 'Arial', sans-serif;
color: #333333;
}
.util-links {
padding: 10px 10px;
text-align: right;
}
a.button, div.button {
color: #6e6e6e;
font: bold 12px Helvetica, Arial, sans-serif;
text-decoration: none;
padding: 7px 12px;
position: relative;
display: inline-block;
text-shadow: 0 1px 0 #fff;
-webkit-transition: border-color .218s;
-moz-transition: border .218s;
-o-transition: border-color .218s;
transition: border-color .218s;
background: #f3f3f3;
background: -webkit-gradient(linear, 0% 40%, 0% 70%, from(#F5F5F5), to(#F1F1F1));
background: -moz-linear-gradient(linear, 0% 40%, 0% 70%, from(#F5F5F5), to(#F1F1F1));
border: solid 1px #dcdcdc;
border-radius: 2px;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
}
a.button:hover {
color: #333;
border-color: #999;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
a.button:active {
color: #000;
border-color: #444;
}
.node rect {
cursor: pointer;
stroke-width: 1.5px;
}
.node text {
font: 10pt sans-serif;
pointer-events: none;
text-overflow: ellipsis;
width: 200px;
}
path.link {
fill: none;
stroke: #9ecae1;
stroke-width: 1.5px;
}
textarea#script {
margin: 1em;
width: 80%;
background-color: #f8f8ff;
padding: 10px 10px;
height: 2em;
/*max-height: 20em;*/
white-space: pre;
}
.CodeMirror-scroll {
width: 100%
}
.CodeMirror {
border: 2px solid #dcdcdc;
}
.activeline {background: #e8f2ff !important;}
</style>
</head>
<body>
<div id="top">
<h2>Execution Plan</h2>
<textarea id="script">
</textarea>
<div class="util-links" id="util-links">
<a href="#" class="button" onclick="render()">visualize</a>
</div>
</div>
<div id="chart"></div>
<script type="text/javascript">
editor = CodeMirror.fromTextArea(document.getElementById('script'), {
lineNumbers: true,
matchBrackets: true,
indentUnit: 4,
mode: "text/x-qlio",
lineWrapping: true
});
var w = 960,
h = 800,
i = 0,
barHeight = 20,
barWidth = w * .6,
duration = 400,
root;
var tree = d3.layout.tree()
.size([h, 100]);
var diagonal = d3.svg.diagonal()
.projection(function (d) {
return [d.y, d.x];
});
var vis = d3.select("#chart").append("svg:svg")
.attr("width", screen.width)
.attr("height", 25000)
.append("svg:g")
.attr("transform", "translate(20,30)");
function draw(json) {
json.x0 = 0;
json.y0 = 0;
update(root = json);
}
var compiler = require('ql.io-compiler');
function render() {
var script = editor.getValue();
var plan = compiler.compile(script);
var links = [];
var top = {};
var depth = 0;
function walk(parent, node) {
depth++;
parent.depth = depth;
parent.children = [];
parent.listeners = node.listeners ? node.listeners.length : 0;
if(node.rhs) {
var child = {};
parent.children.push(child);
walk(child, node.rhs)
}
parent.name = stringify(node);
parent.line = node.line;
if(node.dependsOn) {
for(var i = 0; i < node.dependsOn.length; i++) {
var dependency = node.dependsOn[i];
var child = {};
parent.children.push(child);
walk(child, dependency);
}
}
depth--;
}
walk(top, plan);
draw(top);
}
function stringify(node) {
var str = '[' + node.line + ']';
if(node.assign) {
str = str + ' ' + node.assign + ' = ';
}
switch(node.type) {
case 'create' :
str = str + ' create ' + node.name;
break;
case 'select' :
str = str + ' select from';
node.fromClause.forEach(function(from) {
str = str + ' ' + from.name;
});
if(node.joiner) {
node.joiner.fromClause.forEach(function(from) {
str = str + ' ' + from.name;
});
}
break;
case 'define' :
str = str + ' ' + JSON.stringify(node.object);
break;
case 'return' :
str = str + ' return';
break;
}
return str;
}
var colors = d3.scale.category20();
function update(source) {
// Compute the flattened node list. TODO use d3.layout.hierarchy.
var nodes = tree.nodes(root);
// Compute the "layout".
nodes.forEach(function (n, i) {
n.x = i * barHeight;
});
// Update the nodes…
var node = vis.selectAll("g.node")
.data(nodes, function (d) {
return d.id || (d.id = ++i);
});
var nodeEnter = node.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.style("opacity", 1e-6);
// Enter any new nodes at the parent's previous position.
nodeEnter.append("svg:rect")
.attr("y", -barHeight / 2)
.attr("height", barHeight)
.attr("width", barWidth)
.style("fill", "white")
.on("click", click);
nodeEnter.append("svg:text")
.attr("dy", 3.5)
.attr("dx", 5.5)
.text(function (d) {
return d.name;
});
// Transition nodes to their new position.
nodeEnter.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
})
.style("opacity", 1);
node.transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + d.y + "," + d.x + ")";
})
.style("opacity", 1)
.select("rect")
// Transition exiting nodes to the parent's new position.
node.exit().transition()
.duration(duration)
.attr("transform", function (d) {
return "translate(" + source.y + "," + source.x + ")";
})
.style("opacity", 1e-6)
.remove();
// Update the links…
var link = vis.selectAll("path.link")
.data(tree.links(nodes), function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("svg:path", "g")
.attr("class", "link")
.attr("d", function (d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
})
.transition()
.duration(duration)
.attr("d", diagonal);
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
var hlLine = editor.setLineClass(0, "activeline");
function click(d) {
editor.setLineClass(hlLine, null, null);
editor.setCursor(d.line - 1);
hlLine = editor.setLineClass(editor.getCursor().line, null, "activeline");
if(d.children) {
d._children = d.children;
d.children = null;
}
else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment