execute query

This commit is contained in:
Jan Prochazka
2021-03-08 18:23:41 +01:00
parent 929c08e094
commit 904d35d26a
6 changed files with 285 additions and 7 deletions

View File

@@ -47,6 +47,10 @@
let clientWidth;
let clientHeight;
export function getEditor(): ace.Editor {
return editor;
}
const requireEditorPlugins = () => {};
requireEditorPlugins();

View File

@@ -0,0 +1,96 @@
<script lang="ts" context="module">
function formatDuration(duration) {
if (duration == 0) return '0';
if (duration < 1000) {
return `${Math.round(duration)} ms`;
}
if (duration < 10000) {
return `${Math.round(duration / 100) / 10} s`;
}
return `${Math.round(duration / 1000)} s`;
}
</script>
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import moment from 'moment';
export let items: any[];
export let showProcedure = false;
export let showLine = false;
$: time0 = items[0] && new Date(items[0].time).getTime();
const dispatch = createEventDispatcher();
</script>
<div class="main">
<table>
<tr>
<td class="header">Number</td>
<td class="header">Message</td>
<td class="header">Time</td>
<td class="header">Delta</td>
<td class="header">Duration</td>
{#if showProcedure}
<td class="header">Procedure</td>
{/if}
{#if showLine}
<td class="header">Line</td>
{/if}
</tr>
{#each items as row, index}
<tr
class:isError={row.severity == 'error'}
class:isActive={row.line}
on:click={() => dispatch('messageclick', row)}
>
<td>{index + 1}</td>
<td>{row.message}</td>
<td>{moment(row.time).format('HH:mm:ss')}</td>
<td>{formatDuration(new Date(row.time).getTime() - time0)}</td>
<td>
{index > 0
? formatDuration(new Date(row.time).getTime() - new Date(items[index - 1].time).getTime())
: 'n/a'}</td
>
{#if showProcedure}
<td>{row.procedure || ''}</td>
{/if}
{#if showLine}
<td>{row.line || ''}</td>
{/if}
</tr>
{/each}
</table>
</div>
<style>
.main {
flex: 1;
display: flex;
overflow-y: scroll;
background-color: var(--theme-bg-0);
}
table {
flex: 1;
border-spacing: 0;
border-collapse: collapse;
}
td.header {
text-align: left;
border-bottom: 2px solid var(--theme-border);
background-color: var(--theme-bg-1);
padding: 5px;
}
td:not(.header) {
border-top: 1px solid var(--theme-border);
padding: 5px;
}
tr.isActive:hover {
background: var(--theme-bg-2);
}
tr.isError {
color: var(--theme-icon-red);
}
</style>

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import _ from 'lodash';
import ErrorInfo from '../elements/ErrorInfo.svelte';
import socket from '../utility/socket';
import useEffect from '../utility/useEffect';
import MessageView from './MessageView.svelte';
export let showProcedure = false;
export let showLine = false;
export let eventName;
export let executeNumber;
const cachedMessagesRef = { current: [] };
let displayedMessages = [];
const displayCachedMessages = _.throttle(() => {
console.log('THROTTLE', cachedMessagesRef.current);
displayedMessages = [...cachedMessagesRef.current];
}, 500);
const handleInfo = info => {
console.log('ACCEPTED', info);
cachedMessagesRef.current.push(info);
displayCachedMessages();
};
$: effect = useEffect(() => {
if (eventName) {
socket.on(eventName, handleInfo);
return () => {
socket.off(eventName, handleInfo);
};
}
return () => {};
});
$: {
if (executeNumber >= 0) {
console.log('CLEAR');
displayedMessages = [];
cachedMessagesRef.current = [];
}
}
$: $effect;
$: console.log('displayedMessages', displayedMessages);
</script>
{#if !displayedMessages || displayedMessages.length == 0}
<ErrorInfo message="No messages" icon="img alert" />
{:else}
<MessageView items={displayedMessages} on:messageclick {showProcedure} {showLine} />
{/if}

View File

@@ -8,6 +8,7 @@
<script lang="ts">
import AceEditor from './AceEditor.svelte';
import * as ace from 'ace-builds/src-noconflict/ace';
export let engine;
let domEditor;
@@ -18,9 +19,9 @@
mode = engineToMode[match ? match[1] : engine] || 'sql';
}
export function getSelectedText() {
return domEditor.getSelectedText()
export function getEditor(): ace.Editor {
return domEditor.getEditor();
}
</script>
<AceEditor {mode} {...$$props} on:input on:focus on:blur bind:this={domEditor}/>
<AceEditor {mode} {...$$props} on:input on:focus on:blur bind:this={domEditor} />

View File

@@ -1,5 +1,6 @@
<script lang="ts" context="module">
const currentQuery = writable(null);
const currentQueryStatus = memberStore(currentQuery, query => query?.getStatus() || nullStore);
registerCommand({
id: 'query.execute',
@@ -8,13 +9,29 @@
icon: 'icon run',
toolbar: true,
keyText: 'F5 | Ctrl+Enter',
enabledStore: derived(currentQuery, query => query != null),
enabledStore: derived(
[currentQuery, currentQueryStatus],
([query, status]) => query != null && !(status as any).busy
),
onClick: () => get(currentQuery).execute(),
});
registerCommand({
id: 'query.kill',
category: 'Query',
name: 'Kill',
icon: 'icon close',
toolbar: true,
enabledStore: derived(
[currentQuery, currentQueryStatus],
([query, status]) => query != null && status && (status as any).isConnected
),
onClick: () => get(currentQuery).kill(),
});
</script>
<script lang="ts">
import { get_current_component } from 'svelte/internal';
import { onDestroy } from 'svelte';
import { writable, derived, get } from 'svelte/store';
import registerCommand from '../commands/registerCommand';
@@ -22,9 +39,15 @@
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
import SqlEditor from '../query/SqlEditor.svelte';
import useEditorData from '../query/useEditorData';
import { extensions } from '../stores';
import { extensions, nullStore } from '../stores';
import applySqlTemplate from '../utility/applySqlTemplate';
import axios from '../utility/axios';
import { changeTab } from '../utility/common';
import { useConnectionInfo } from '../utility/metadataLoaders';
import socket from '../utility/socket';
import SocketMessageView from '../query/SocketMessageView.svelte';
import memberStore from '../utility/memberStore';
import useEffect from '../utility/useEffect';
export let tabid;
export let conid;
@@ -33,9 +56,92 @@
const instance = get_current_component();
let busy = false;
let executeNumber = 0;
let visibleResultTabs = false;
let sessionId = null;
let domEditor;
const status = writable({
busy,
isConnected: false,
});
$: connection = useConnectionInfo({ conid });
const { editorState, setEditorData } = useEditorData({
$: effect = useEffect(() => {
if (sessionId) {
const sid = sessionId;
socket.on(`session-done-${sid}`, handleSessionDone);
return () => {
socket.off(`session-done-${sid}`, handleSessionDone);
};
}
return () => {};
});
$: $effect;
$: {
changeTab(tabid, tab => ({ ...tab, busy }));
}
$: {
status.set({
busy,
isConnected: !!sessionId,
});
}
export async function execute() {
if (busy) return;
executeNumber++;
visibleResultTabs = true;
const selectedText = domEditor.getEditor().getSelectedText();
let sesid = sessionId;
if (!sesid) {
const resp = await axios.post('sessions/create', {
conid,
database,
});
sesid = resp.data.sesid;
sessionId = sesid;
}
busy = true;
// timerLabel.start();
await axios.post('sessions/execute-query', {
sesid,
sql: selectedText || $editorValue,
});
}
export async function kill() {
await axios.post('sessions/kill', {
sesid: sessionId,
});
sessionId = null;
busy = false;
// timerLabel.stop();
}
export function getStatus() {
return status;
}
const handleMesageClick = message => {
// console.log('EDITOR', editorRef.current.editor);
if (domEditor.getEditor()) {
domEditor.getEditor().gotoLine(message.line);
}
};
const handleSessionDone = () => {
busy = false;
// timerLabel.stop();
};
const { editorState, editorValue, setEditorData } = useEditorData({
tabid,
loadFromArgs:
initialArgs && initialArgs.sqlTemplate
@@ -44,13 +150,23 @@
});
</script>
<VerticalSplitter>
<VerticalSplitter isSplitter={visibleResultTabs}>
<svelte:fragment slot="1">
<SqlEditor
engine={$connection && $connection.engine}
value={$editorState.value || ''}
on:input={e => setEditorData(e.detail)}
on:focus={() => currentQuery.set(instance)}
bind:this={domEditor}
/>
</svelte:fragment>
<svelte:fragment slot="2">
<SocketMessageView
eventName={sessionId ? `session-info-${sessionId}` : null}
on:messageClick={handleMesageClick}
{executeNumber}
showProcedure
showLine
/>
</svelte:fragment>
</VerticalSplitter>

View File

@@ -0,0 +1,3 @@
const useEffect = subscribe => ({ subscribe });
export default useEffect;