mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-20 22:55:59 +00:00
execute query
This commit is contained in:
@@ -47,6 +47,10 @@
|
|||||||
let clientWidth;
|
let clientWidth;
|
||||||
let clientHeight;
|
let clientHeight;
|
||||||
|
|
||||||
|
export function getEditor(): ace.Editor {
|
||||||
|
return editor;
|
||||||
|
}
|
||||||
|
|
||||||
const requireEditorPlugins = () => {};
|
const requireEditorPlugins = () => {};
|
||||||
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">
|
<script lang="ts">
|
||||||
import AceEditor from './AceEditor.svelte';
|
import AceEditor from './AceEditor.svelte';
|
||||||
|
import * as ace from 'ace-builds/src-noconflict/ace';
|
||||||
export let engine;
|
export let engine;
|
||||||
let domEditor;
|
let domEditor;
|
||||||
|
|
||||||
@@ -18,8 +19,8 @@
|
|||||||
mode = engineToMode[match ? match[1] : engine] || 'sql';
|
mode = engineToMode[match ? match[1] : engine] || 'sql';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectedText() {
|
export function getEditor(): ace.Editor {
|
||||||
return domEditor.getSelectedText()
|
return domEditor.getEditor();
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" context="module">
|
<script lang="ts" context="module">
|
||||||
const currentQuery = writable(null);
|
const currentQuery = writable(null);
|
||||||
|
const currentQueryStatus = memberStore(currentQuery, query => query?.getStatus() || nullStore);
|
||||||
|
|
||||||
registerCommand({
|
registerCommand({
|
||||||
id: 'query.execute',
|
id: 'query.execute',
|
||||||
@@ -8,13 +9,29 @@
|
|||||||
icon: 'icon run',
|
icon: 'icon run',
|
||||||
toolbar: true,
|
toolbar: true,
|
||||||
keyText: 'F5 | Ctrl+Enter',
|
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(),
|
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>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { get_current_component } from 'svelte/internal';
|
import { get_current_component } from 'svelte/internal';
|
||||||
|
import { onDestroy } from 'svelte';
|
||||||
|
|
||||||
import { writable, derived, get } from 'svelte/store';
|
import { writable, derived, get } from 'svelte/store';
|
||||||
import registerCommand from '../commands/registerCommand';
|
import registerCommand from '../commands/registerCommand';
|
||||||
@@ -22,9 +39,15 @@
|
|||||||
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
import VerticalSplitter from '../elements/VerticalSplitter.svelte';
|
||||||
import SqlEditor from '../query/SqlEditor.svelte';
|
import SqlEditor from '../query/SqlEditor.svelte';
|
||||||
import useEditorData from '../query/useEditorData';
|
import useEditorData from '../query/useEditorData';
|
||||||
import { extensions } from '../stores';
|
import { extensions, nullStore } from '../stores';
|
||||||
import applySqlTemplate from '../utility/applySqlTemplate';
|
import applySqlTemplate from '../utility/applySqlTemplate';
|
||||||
|
import axios from '../utility/axios';
|
||||||
|
import { changeTab } from '../utility/common';
|
||||||
import { useConnectionInfo } from '../utility/metadataLoaders';
|
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 tabid;
|
||||||
export let conid;
|
export let conid;
|
||||||
@@ -33,9 +56,92 @@
|
|||||||
|
|
||||||
const instance = get_current_component();
|
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 });
|
$: 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,
|
tabid,
|
||||||
loadFromArgs:
|
loadFromArgs:
|
||||||
initialArgs && initialArgs.sqlTemplate
|
initialArgs && initialArgs.sqlTemplate
|
||||||
@@ -44,13 +150,23 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<VerticalSplitter>
|
<VerticalSplitter isSplitter={visibleResultTabs}>
|
||||||
<svelte:fragment slot="1">
|
<svelte:fragment slot="1">
|
||||||
<SqlEditor
|
<SqlEditor
|
||||||
engine={$connection && $connection.engine}
|
engine={$connection && $connection.engine}
|
||||||
value={$editorState.value || ''}
|
value={$editorState.value || ''}
|
||||||
on:input={e => setEditorData(e.detail)}
|
on:input={e => setEditorData(e.detail)}
|
||||||
on:focus={() => currentQuery.set(instance)}
|
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>
|
</svelte:fragment>
|
||||||
</VerticalSplitter>
|
</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