Fix race condition; history and sharing capabilities

This commit is contained in:
Alexey Milovidov 2020-11-23 09:35:08 +03:00
parent 711f64048b
commit 2c982b4ccf

View File

@ -30,17 +30,6 @@
It can be done in background, e.g. wait 100 ms after address/credentials change and do the check. It can be done in background, e.g. wait 100 ms after address/credentials change and do the check.
Also it can provide visual indication that credentials are correct. Also it can provide visual indication that credentials are correct.
3. Add history in localstorage. Integrate with history API.
There can be a counter in localstorage, that will be appended to location #fragment.
The 'back', 'forward' buttons in browser should work.
Also there should be UI element to list all the queries from history and select from the list.
4. Trivial sharing capabilities.
Sharing is only possible when system.query_log is accessible. Read the X-ClickHouse-QueryId from the response.
Share button will: - emit SYSTEM FLUSH LOGS if not readonly; - find the query in the query_log;
- generate an URL with the query id and: server address if not equal to the URL's host; user name if not default;
indication that password should be entered in case of non-empty password.
--> -->
<style type="text/css"> <style type="text/css">
@ -299,14 +288,21 @@
<script type="text/javascript"> <script type="text/javascript">
/// Incremental request number. When response is received,
/// if it's request number does not equal to the current request number, response will be ignored.
/// This is to avoid race conditions.
var request_num = 0;
/// Save query in history only if it is different.
var previous_query = '';
/// Substitute the address of the server where the page is served. /// Substitute the address of the server where the page is served.
if (location.protocol != 'file:') { if (location.protocol != 'file:') {
document.getElementById('url').value = location.origin; document.getElementById('url').value = location.origin;
} }
function post() function postImpl(posted_request_num, query)
{ {
/// TODO: Avoid race condition on subsequent requests when responses may come out of order.
/// TODO: Check if URL already contains query string (append parameters). /// TODO: Check if URL already contains query string (append parameters).
var url = document.getElementById('url').value + var url = document.getElementById('url').value +
@ -318,7 +314,6 @@
/// Safety settings to prevent results that browser cannot display. /// Safety settings to prevent results that browser cannot display.
'&max_result_rows=1000&max_result_bytes=10000000&result_overflow_mode=break'; '&max_result_rows=1000&max_result_bytes=10000000&result_overflow_mode=break';
var query = document.getElementById('query').value;
var xhr = new XMLHttpRequest; var xhr = new XMLHttpRequest;
xhr.open('POST', url, true); xhr.open('POST', url, true);
@ -326,18 +321,25 @@
xhr.onreadystatechange = function() xhr.onreadystatechange = function()
{ {
if (this.readyState === XMLHttpRequest.DONE) { if (posted_request_num != request_num) {
if (this.status === 200) { return;
var json; } else if (this.readyState === XMLHttpRequest.DONE) {
try { json = JSON.parse(this.response); } catch (e) {} renderResponse(this.status, this.response);
if (json !== undefined && json.statistics !== undefined) {
renderResult(json); /// The query is saved in browser history (in state JSON object)
} else { /// as well as in URL fragment identifier.
renderUnparsedResult(this.response); if (query != previous_query) {
} previous_query = query;
} else { var title = "ClickHouse Query: " + query;
/// TODO: Proper rendering of network errors. history.pushState(
renderError(this.response); {
query: query,
status: this.status,
response: this.response.length > 100000 ? null : this.response /// Lower than the browser's limit.
},
title,
window.location.pathname + '#' + window.btoa(query));
document.title = title;
} }
} else { } else {
//console.log(this); //console.log(this);
@ -345,6 +347,44 @@
} }
} }
function renderResponse(status, response) {
if (status === 200) {
var json;
try { json = JSON.parse(response); } catch (e) {}
if (json !== undefined && json.statistics !== undefined) {
renderResult(json);
} else {
renderUnparsedResult(response);
}
} else {
/// TODO: Proper rendering of network errors.
renderError(response);
}
}
window.onpopstate = function(event) {
if (!event.state) {
return;
}
document.getElementById('query').value = event.state.query;
if (!event.state.response) {
clear();
return;
}
renderResponse(event.state.status, event.state.response);
};
if (window.location.hash) {
document.getElementById('query').value = window.atob(window.location.hash.substr(1));
}
function post()
{
++request_num;
var query = document.getElementById('query').value;
postImpl(request_num, query);
}
document.getElementById('run').onclick = function() document.getElementById('run').onclick = function()
{ {
post(); post();