mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 00:46:01 +00:00
execute query
This commit is contained in:
@@ -47,6 +47,10 @@
|
||||
let clientWidth;
|
||||
let clientHeight;
|
||||
|
||||
export function getEditor(): ace.Editor {
|
||||
return editor;
|
||||
}
|
||||
|
||||
const requireEditorPlugins = () => {};
|
||||
requireEditorPlugins();
|
||||
|
||||
|
||||
96
packages/web/src/query/MessageView.svelte
Normal file
96
packages/web/src/query/MessageView.svelte
Normal 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>
|
||||
58
packages/web/src/query/SocketMessageView.svelte
Normal file
58
packages/web/src/query/SocketMessageView.svelte
Normal 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}
|
||||
@@ -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} />
|
||||
|
||||
@@ -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>
|
||||
|
||||
3
packages/web/src/utility/useEffect.ts
Normal file
3
packages/web/src/utility/useEffect.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
const useEffect = subscribe => ({ subscribe });
|
||||
|
||||
export default useEffect;
|
||||
Reference in New Issue
Block a user