From 1a7f06342fc7633c960155b1e1e83ffe2590a3b7 Mon Sep 17 00:00:00 2001 From: Jan Prochazka Date: Sun, 25 Sep 2022 19:45:47 +0200 Subject: [PATCH] query error markers --- packages/api/src/proc/sessionProcess.js | 20 +++++-- .../web/src/datagrid/JslDataGridCore.svelte | 2 +- packages/web/src/query/AceEditor.svelte | 55 ++++++++++++++----- packages/web/src/query/MessageView.svelte | 3 +- .../web/src/query/SocketMessageView.svelte | 20 ++++++- packages/web/src/query/SqlEditor.svelte | 2 +- packages/web/src/tabs/QueryTab.svelte | 27 ++++++--- .../src/backend/tediousDriver.js | 4 +- .../src/backend/drivers.js | 5 +- 9 files changed, 102 insertions(+), 36 deletions(-) diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 2c179c41..91800d7c 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -101,8 +101,9 @@ class TableWriter { } class StreamHandler { - constructor(resultIndexHolder, resolve) { + constructor(resultIndexHolder, resolve, startLine) { this.recordset = this.recordset.bind(this); + this.startLine = startLine; this.row = this.row.bind(this); // this.error = this.error.bind(this); this.done = this.done.bind(this); @@ -155,14 +156,20 @@ class StreamHandler { this.resolve(); } info(info) { + if (info.line != null) { + info = { + ...info, + line: this.startLine + info.line, + }; + } process.send({ msgtype: 'info', info }); } } -function handleStream(driver, resultIndexHolder, sql) { +function handleStream(driver, resultIndexHolder, sqlItem) { return new Promise((resolve, reject) => { - const handler = new StreamHandler(resultIndexHolder, resolve); - driver.stream(systemConnection, sql, handler); + const handler = new StreamHandler(resultIndexHolder, resolve, sqlItem.trimStart.line); + driver.stream(systemConnection, sqlItem.text, handler); }); } @@ -221,7 +228,10 @@ async function handleExecuteQuery({ sql }) { const resultIndexHolder = { value: 0, }; - for (const sqlItem of splitQuery(sql, driver.getQuerySplitterOptions('stream'))) { + for (const sqlItem of splitQuery(sql, { + ...driver.getQuerySplitterOptions('stream'), + returnRichInfo: true, + })) { await handleStream(driver, resultIndexHolder, sqlItem); // const handler = new StreamHandler(resultIndex); // const stream = await driver.stream(systemConnection, sqlItem, handler); diff --git a/packages/web/src/datagrid/JslDataGridCore.svelte b/packages/web/src/datagrid/JslDataGridCore.svelte index 94ea6df2..cb2afde3 100644 --- a/packages/web/src/datagrid/JslDataGridCore.svelte +++ b/packages/web/src/datagrid/JslDataGridCore.svelte @@ -65,7 +65,7 @@ let changeIndex = 0; let rowCountLoaded = null; - const throttleLoadNext = _.throttle(() => domGrid.resetLoadedAll(), 500); + const throttleLoadNext = _.throttle(() => domGrid?.resetLoadedAll(), 500); const handleJslDataStats = stats => { if (stats.changeIndex < changeIndex) return; diff --git a/packages/web/src/query/AceEditor.svelte b/packages/web/src/query/AceEditor.svelte index 58a2ff40..f3e02e83 100644 --- a/packages/web/src/query/AceEditor.svelte +++ b/packages/web/src/query/AceEditor.svelte @@ -156,6 +156,7 @@ export let splitterOptions = null; export let onKeyDown = null; export let onExecuteFragment = null; + export let errorMessages = null; const tabVisible: any = getContext('tabVisible'); @@ -184,16 +185,28 @@ return editor; } - export function getCurrentCommandText(): string { - if (currentPart != null) return currentPart.text; - if (!editor) return ''; + export function getCurrentCommandText(): { text: string; line?: number } { + if (currentPart != null) { + return { + text: currentPart.text, + line: currentPart.trimStart.line, + }; + } + if (!editor) return { text: '' }; const selectedText = editor.getSelectedText(); - if (selectedText) return selectedText; + if (selectedText) + return { + text: selectedText, + line: editor.getSelectionRange().start.row, + }; if (editor.getHighlightActiveLine()) { const line = editor.getSelectionRange().start.row; - return editor.session.getLine(line); + return { + text: editor.session.getLine(line), + line, + }; } - return ''; + return { text: '' }; } export function getCodeCompletionCommandText() { @@ -292,13 +305,25 @@ } function updateAnnotations() { - editor.session.setAnnotations( - (queryParts || []).map(part => ({ - row: part.trimStart.line, - text: part.text, - className: 'ace-gutter-sql-run', - })) - ); + editor?.session?.setAnnotations([ + ...(queryParts || []) + .filter(part => !(errorMessages || []).find(err => err.line == part.trimStart.line)) + .map(part => ({ + row: part.trimStart.line, + text: part.text, + className: 'ace-gutter-sql-run', + })), + ...(errorMessages || []).map(error => ({ + row: error.line, + text: error.message, + type: 'error', + })), + ]); + } + + $: { + errorMessages; + updateAnnotations(); } const handleContextMenu = e => { @@ -455,10 +480,10 @@ const part = (queryParts || []).find(part => part.trimStart.line == row); if (part && onExecuteFragment) { - onExecuteFragment(part.text); + onExecuteFragment(part.text, part.trimStart.line); e.stop(); editor.moveCursorTo(part.trimStart.line, 0); - editor.selection.clearSelection() + editor.selection.clearSelection(); } }, true diff --git a/packages/web/src/query/MessageView.svelte b/packages/web/src/query/MessageView.svelte index 99d47cc7..20d0a6e0 100644 --- a/packages/web/src/query/MessageView.svelte +++ b/packages/web/src/query/MessageView.svelte @@ -18,6 +18,7 @@ export let items: any[]; export let showProcedure = false; export let showLine = false; + export let startLine = 0; $: time0 = items[0] && new Date(items[0].time).getTime(); @@ -58,7 +59,7 @@ {row.procedure || ''} {/if} {#if showLine} - {row.line || ''} + {row.line == null ? '' : row.line + 1 + startLine} {/if} {/each} diff --git a/packages/web/src/query/SocketMessageView.svelte b/packages/web/src/query/SocketMessageView.svelte index cc48ffcf..74686375 100644 --- a/packages/web/src/query/SocketMessageView.svelte +++ b/packages/web/src/query/SocketMessageView.svelte @@ -13,8 +13,11 @@ export let eventName; export let executeNumber; export let showNoMessagesAlert = false; + export let startLine = 0; + export let onChangeErrors = null; const cachedMessagesRef = createRef([]); + const lastErrorMessageCountRef = createRef(0); let displayedMessages = []; @@ -44,11 +47,26 @@ } } + $: { + if (onChangeErrors) { + const errors = displayedMessages.filter(x => x.severity == 'error'); + if (lastErrorMessageCountRef.get() != errors.length) { + onChangeErrors( + errors.map(err => ({ + ...err, + line: err.line == null ? null : err.line + startLine, + })) + ); + lastErrorMessageCountRef.set(errors.length); + } + } + } + $: $effect; {#if showNoMessagesAlert && (!displayedMessages || displayedMessages.length == 0)} {:else} - + {/if} diff --git a/packages/web/src/query/SqlEditor.svelte b/packages/web/src/query/SqlEditor.svelte index 68fc206f..b7870756 100644 --- a/packages/web/src/query/SqlEditor.svelte +++ b/packages/web/src/query/SqlEditor.svelte @@ -32,7 +32,7 @@ return domEditor.getEditor(); } - export function getCurrentCommandText(): string { + export function getCurrentCommandText(): { text: string; line?: number } { return domEditor.getCurrentCommandText(); } diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index 5b079eef..e925dc09 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -86,10 +86,11 @@ let busy = false; let executeNumber = 0; + let executeStartLine = 0; let visibleResultTabs = false; let sessionId = null; let resultCount; - + let errorMessages; let domEditor; $: connection = useConnectionInfo({ conid }); @@ -143,13 +144,14 @@ return !!conid && (!$connection?.isReadOnly || driver?.readOnlySessions); } - async function executeCore(sql) { + async function executeCore(sql, startLine = 0) { if (busy) return; if (!sql || !sql.trim()) { showSnackbarError('Skipped executing empty query'); return; } + executeStartLine = startLine; executeNumber++; visibleResultTabs = true; @@ -179,13 +181,14 @@ } export async function executeCurrent() { - const sql = domEditor.getCurrentCommandText(); - await executeCore(sql); + const cmd = domEditor.getCurrentCommandText(); + await executeCore(cmd.text, cmd.line); } export async function execute() { const selectedText = domEditor.getEditor().getSelectedText(); - await executeCore(selectedText || $editorValue); + const startLine = domEditor.getEditor().getSelectionRange().start.row; + await executeCore(selectedText || $editorValue, selectedText ? startLine : 0); } export async function kill() { @@ -257,6 +260,10 @@ : null, }); + function handleChangeErrors(errors) { + errorMessages = errors; + } + function createMenu() { return [ { command: 'query.execute' }, @@ -289,13 +296,17 @@ splitterOptions={driver?.getQuerySplitterOptions('script')} value={$editorState.value || ''} menu={createMenu()} - on:input={e => setEditorData(e.detail)} + on:input={e => { + setEditorData(e.detail); + errorMessages = []; + }} on:focus={() => { activator.activate(); invalidateCommands(); }} bind:this={domEditor} - onExecuteFragment={sql => executeCore(sql)} + onExecuteFragment={(sql, startLine) => executeCore(sql, startLine)} + {errorMessages} /> {:else} diff --git a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js index 4389667d..45e62d12 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js @@ -134,7 +134,7 @@ async function tediousStream(pool, sql, options) { const { message, lineNumber, procName } = info; options.info({ message, - line: lineNumber, + line: lineNumber != null && lineNumber > 0 ? lineNumber - 1 : lineNumber, procedure: procName, time: new Date(), severity: 'info', @@ -144,7 +144,7 @@ async function tediousStream(pool, sql, options) { const { message, lineNumber, procName } = error; options.info({ message, - line: lineNumber, + line: lineNumber != null && lineNumber > 0 ? lineNumber - 1 : lineNumber, procedure: procName, time: new Date(), severity: 'error', diff --git a/plugins/dbgate-plugin-mysql/src/backend/drivers.js b/plugins/dbgate-plugin-mysql/src/backend/drivers.js index 5c5bbbbd..587db33f 100644 --- a/plugins/dbgate-plugin-mysql/src/backend/drivers.js +++ b/plugins/dbgate-plugin-mysql/src/backend/drivers.js @@ -112,11 +112,10 @@ const drivers = driverBases.map(driverBase => ({ const handleError = error => { console.log('ERROR', error); - const { message, lineNumber, procName } = error; + const { message } = error; options.info({ message, - line: lineNumber, - procedure: procName, + line: 0, time: new Date(), severity: 'error', });