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',
});