ClickHouse/programs/server/play.html
2020-10-19 22:25:58 +03:00

331 lines
9.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<html> <!-- TODO If I write DOCTYPE HTML something changes but I don't know what. -->
<head>
<meta charset="UTF-8">
<title>ClickHouse Query</title>
<!-- Code style:
Do not use any JavaScript or CSS frameworks or preprocessors.
This HTML page should not require any build systems (node.js, npm, gulp, etc.)
This HTML page should not be minified, instead it should be reasonably minimalistic by itself.
This HTML page should not load any external resources
(CSS and JavaScript must be embedded directly to the page. No external fonts or images should be loaded).
This UI should look as lightweight, clean and fast as possible.
All UI elements must be aligned in pixel-perfect way.
There should not be any animations.
-->
<style type="text/css">
html, body
{
/* Personal choice. */
font-family: Sans-Serif;
/* Or #FFFBEF; actually many pastel colors look great for light theme. TODO Provide dark theme. */
background: #DDF8FF;
}
/* Otherwise Webkit based browsers will display ugly border on focus. */
textarea, input
{
outline: none;
}
/* Otherwise scrollbar may appear dynamically and it will alter viewport height,
then relative heights of elements will change suddenly, and it will break overall impression. */
/* html
{
overflow-x: scroll;
}*/
div
{
width: 100%;
}
.monospace
{
/* Prefer fonts that have full hinting info. This is important for non-retina displays.
Also I personally dislike "Ubuntu" font due to the similarity of 'r' and 'г' (it looks very ignorant).
*/
font-family: Liberation Mono, DejaVu Sans Mono, MonoLisa, Consolas, Monospace;
}
.shadow
{
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
}
input, textarea
{
border: 1px solid #EEE;
/* The font must be not too small (to be inclusive) and not too large (it's less practical and make general feel of insecurity) */
font-size: 11pt;
padding: 0.25rem;
background-color: #FFF;
}
#query
{
/* Make enough space for even huge queries. */
height: 20%;
width: 100%;
}
#inputs
{
white-space: nowrap;
width: 100%;
}
#url
{
width: 70%;
}
#user
{
width: 15%;
}
#password
{
width: 15%;
}
#run_div
{
margin-top: 1rem;
}
/* Orange on light-cyan is especially good. */
#run
{
color: #000;
background-color: #FFAA00;
padding: 0.25rem 1rem;
cursor: pointer;
font-weight: bold;
}
#run:hover
{
color: #FFF;
background-color: #F00;
}
#stats
{
float: right;
color: #888;
}
.hint
{
color: #888;
}
#data_div
{
margin-top: 1rem;
}
#data-table
{
border-collapse: collapse;
border-spacing: 0;
/* I need pixel-perfect alignment but not sure the following is correct, please help */
min-width: calc(100vw - 2rem);
}
td
{
background-color: #FFF;
white-space: nowrap;
/* For wide tables any individual column will be no more than 50% of page width. */
max-width: 50vw;
/* The content is cut unless you hover. */
overflow: hidden;
padding: 0.25rem 0.5rem;
border: 1px solid #EEE;
white-space: pre;
}
td.right
{
text-align: right;
}
th
{
padding: 0.25rem 0.5rem;
text-align: middle;
background-color: #F8F8F8;
border: 1px solid #EEE;
}
/* The row under mouse pointer is highlight for better legibility. */
tr:hover, tr:hover td
{
background-color: #FFF8EF;
}
tr:hover
{
box-shadow: 0 0 1rem rgba(0, 0, 0, 0.1);
}
/* Light-pink on light-cyan is so neat, I even want to trigger errors to see this cool combination of colors. */
#error
{
background: #FEE;
white-space: pre-wrap;
padding: 0.5rem 1rem;
display: none;
}
/* When mouse pointer is over table cell, will display full text (with wrap) instead of cut.
TODO Find a way to make it work on touch devices. */
td.left:hover
{
white-space: pre-wrap;
}
/* The style for SQL NULL */
.null
{
color: #A88;
}
</style>
</head>
<body>
<div id="inputs">
<input class="monospace shadow" id="url" type="text" value="http://localhost:8123/" /><input class="monospace shadow" id="user" type="text" value="default" /><input class="monospace shadow" id="password" type="password" />
</div>
<div>
<textarea class="monospace shadow" id="query"></textarea>
</div>
<div id="run_div">
<span class="shadow" id="run">Run</span>
<span class="hint">&nbsp;(Ctrl+Enter)</span>
<span id="stats"></span>
</div>
<div id="data_div">
<table class="monospace shadow" id="data-table">
</table>
</div>
<p id="error" class="monospace shadow">
</p>
</body>
<script type="text/javascript">
function post()
{
var url = document.getElementById('url').value +
'?add_http_cors_header=1' +
'&user=' + encodeURIComponent(document.getElementById('user').value) +
'&password=' + encodeURIComponent(document.getElementById('password').value) +
'&default_format=JSONCompact' +
'&max_result_rows=1000&max_result_bytes=10000000&result_overflow_mode=break';
var query = document.getElementById('query').value;
var xhr = new XMLHttpRequest;
xhr.open('POST', url, true);
xhr.send(query);
xhr.onreadystatechange = function()
{
if (this.readyState === XMLHttpRequest.DONE) {
if (this.status === 200) {
renderResult(JSON.parse(this.response));
} else {
renderError(this.response);
}
} else {
console.log(this);
}
}
}
document.getElementById('run').onclick = function()
{
post();
}
document.getElementById('query').onkeypress = function(event)
{
if (event.ctrlKey && (event.charCode == 13 || event.charCode == 10)) {
post();
}
}
function clear()
{
var table = document.getElementById('data-table');
while (table.firstChild) {
table.removeChild(table.lastChild);
}
document.getElementById('error').innerText = '';
document.getElementById('error').style.display = 'none';
}
function renderResult(response)
{
//console.log(response);
clear();
var stats = document.getElementById('stats');
stats.innerText = 'Elapsed: ' + response.statistics.elapsed.toFixed(3) + " sec, read " + response.statistics.rows_read + " rows.";
var thead = document.createElement('thead');
for (var idx in response.meta) {
var th = document.createElement('th');
var name = document.createTextNode(response.meta[idx].name);
th.appendChild(name);
thead.appendChild(th);
}
var max_rows = 10000 / response.meta.length;
var row_num = 0;
var tbody = document.createElement('tbody');
for (var row_idx in response.data) {
var tr = document.createElement('tr');
for (var col_idx in response.data[row_idx]) {
var td = document.createElement('td');
var cell = response.data[row_idx][col_idx];
var is_null = (cell === null);
var content = document.createTextNode(is_null ? 'ᴺᵁᴸᴸ' : cell);
td.appendChild(content);
td.className = response.meta[col_idx].type.match(/^(U?Int|Decimal|Float)/) ? 'right' : 'left';
if (is_null) {
td.className += ' null';
}
tr.appendChild(td);
}
tbody.appendChild(tr);
++row_num;
if (row_num >= max_rows) {
break;
}
}
var table = document.getElementById('data-table');
table.appendChild(thead);
table.appendChild(tbody);
}
function renderError(response)
{
clear();
document.getElementById('error').innerText = response;
document.getElementById('error').style.display = 'block';
}
</script>
</html>