From ef7f050bc5bfd79ef4d385cebde02df190696970 Mon Sep 17 00:00:00 2001 From: "SPRINX0\\prochazka" Date: Wed, 5 Nov 2025 12:11:19 +0100 Subject: [PATCH] Show executed query on log #1236 --- .../src/controllers/databaseConnections.js | 126 +++++++++++------- packages/api/src/controllers/sessions.js | 5 +- .../web/src/elements/SqlHighlighter.svelte | 12 +- packages/web/src/modals/ShowSqlModal.svelte | 53 ++++++++ packages/web/src/plugins/ThemeDark.svelte | 2 +- packages/web/src/query/MessageView.svelte | 2 + packages/web/src/query/MessageViewRow.svelte | 18 ++- .../web/src/query/SocketMessageView.svelte | 2 + packages/web/src/tabs/QueryTab.svelte | 1 + 9 files changed, 169 insertions(+), 52 deletions(-) create mode 100644 packages/web/src/modals/ShowSqlModal.svelte diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 383531e02..161ab1e32 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -29,7 +29,17 @@ const generateDeploySql = require('../shell/generateDeploySql'); const { createTwoFilesPatch } = require('diff'); const diff2htmlPage = require('../utility/diff2htmlPage'); const processArgs = require('../utility/processArgs'); -const { testConnectionPermission, hasPermission, loadPermissionsFromRequest, loadTablePermissionsFromRequest, getTablePermissionRole, loadDatabasePermissionsFromRequest, getDatabasePermissionRole, getTablePermissionRoleLevelIndex, testDatabaseRolePermission } = require('../utility/hasPermission'); +const { + testConnectionPermission, + hasPermission, + loadPermissionsFromRequest, + loadTablePermissionsFromRequest, + getTablePermissionRole, + loadDatabasePermissionsFromRequest, + getDatabasePermissionRole, + getTablePermissionRoleLevelIndex, + testDatabaseRolePermission, +} = require('../utility/hasPermission'); const { MissingCredentialsError } = require('../utility/exceptions'); const pipeForkLogs = require('../utility/pipeForkLogs'); const crypto = require('crypto'); @@ -100,7 +110,7 @@ module.exports = { socket.emitChanged(`database-status-changed`, { conid, database }); }, - handle_ping() { }, + handle_ping() {}, // session event handlers @@ -256,23 +266,24 @@ module.exports = { auditLogger: auditLogSessionGroup && select?.from?.name?.pureName ? response => { - sendToAuditLog(req, { - category: 'dbop', - component: 'DatabaseConnectionsController', - event: 'sql.select', - action: 'select', - severity: 'info', - conid, - database, - schemaName: select?.from?.name?.schemaName, - pureName: select?.from?.name?.pureName, - sumint1: response?.rows?.length, - sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${select?.from?.name?.pureName + sendToAuditLog(req, { + category: 'dbop', + component: 'DatabaseConnectionsController', + event: 'sql.select', + action: 'select', + severity: 'info', + conid, + database, + schemaName: select?.from?.name?.schemaName, + pureName: select?.from?.name?.pureName, + sumint1: response?.rows?.length, + sessionParam: `${conid}::${database}::${select?.from?.name?.schemaName || '0'}::${ + select?.from?.name?.pureName }`, - sessionGroup: auditLogSessionGroup, - message: `Loaded table data from ${select?.from?.name?.pureName}`, - }); - } + sessionGroup: auditLogSessionGroup, + message: `Loaded table data from ${select?.from?.name?.pureName}`, + }); + } : null, } ); @@ -335,21 +346,21 @@ module.exports = { auditLogger: auditLogSessionGroup && options?.pureName ? response => { - sendToAuditLog(req, { - category: 'dbop', - component: 'DatabaseConnectionsController', - event: 'nosql.collectionData', - action: 'select', - severity: 'info', - conid, - database, - pureName: options?.pureName, - sumint1: response?.result?.rows?.length, - sessionParam: `${conid}::${database}::${options?.pureName}`, - sessionGroup: auditLogSessionGroup, - message: `Loaded collection data ${options?.pureName}`, - }); - } + sendToAuditLog(req, { + category: 'dbop', + component: 'DatabaseConnectionsController', + event: 'nosql.collectionData', + action: 'select', + severity: 'info', + conid, + database, + pureName: options?.pureName, + sumint1: response?.result?.rows?.length, + sessionParam: `${conid}::${database}::${options?.pureName}`, + sessionGroup: auditLogSessionGroup, + message: `Loaded collection data ${options?.pureName}`, + }); + } : null, } ); @@ -455,10 +466,18 @@ module.exports = { [changeSet.inserts, 'create_update_delete'], [changeSet.deletes, 'create_update_delete'], [changeSet.updates, 'update_only'], - ] + ]; for (const [operations, requiredRole] of fieldsAndRoles) { for (const operation of operations) { - const role = getTablePermissionRole(conid, database, 'tables', operation.schemaName, operation.pureName, tablePermissions, databasePermissions); + const role = getTablePermissionRole( + conid, + database, + 'tables', + operation.schemaName, + operation.pureName, + tablePermissions, + databasePermissions + ); if (getTablePermissionRoleLevelIndex(role) < getTablePermissionRoleLevelIndex(requiredRole)) { throw new Error('DBGM-00262 Permission not granted'); } @@ -628,7 +647,15 @@ module.exports = { function applyTablePermissionRole(list, objectTypeField) { const res = []; for (const item of list ?? []) { - const tablePermissionRole = getTablePermissionRole(conid, database, objectTypeField, item.schemaName, item.pureName, tablePermissions, databasePermissionRole); + const tablePermissionRole = getTablePermissionRole( + conid, + database, + objectTypeField, + item.schemaName, + item.pureName, + tablePermissions, + databasePermissionRole + ); if (tablePermissionRole != 'deny') { res.push({ ...item, @@ -647,7 +674,7 @@ module.exports = { functions: applyTablePermissionRole(opened.structure.functions, 'functions'), triggers: applyTablePermissionRole(opened.structure.triggers, 'triggers'), collections: applyTablePermissionRole(opened.structure.collections, 'collections'), - } + }; return res; } @@ -881,17 +908,17 @@ module.exports = { return { ...(command == 'backup' ? driver.backupDatabaseCommand( - connection, - { outputFile, database, options, selectedTables, skippedTables, argsFormat }, - // @ts-ignore - externalTools - ) + connection, + { outputFile, database, options, selectedTables, skippedTables, argsFormat }, + // @ts-ignore + externalTools + ) : driver.restoreDatabaseCommand( - connection, - { inputFile, database, options, argsFormat }, - // @ts-ignore - externalTools - )), + connection, + { inputFile, database, options, argsFormat }, + // @ts-ignore + externalTools + )), transformMessage: driver.transformNativeCommandMessage ? message => driver.transformNativeCommandMessage(message, command) : null, @@ -990,7 +1017,10 @@ module.exports = { async executeSessionQuery({ sesid, conid, database, sql }, req) { await testConnectionPermission(conid, req); logger.info({ sesid, sql }, 'DBGM-00010 Processing query'); - sessions.dispatchMessage(sesid, 'Query execution started'); + sessions.dispatchMessage(sesid, { + message: 'Query execution started', + sql, + }); const opened = await this.ensureOpened(conid, database); opened.subprocess.send({ msgtype: 'executeSessionQuery', sql, sesid }); diff --git a/packages/api/src/controllers/sessions.js b/packages/api/src/controllers/sessions.js index f05034bb9..89278a7e9 100644 --- a/packages/api/src/controllers/sessions.js +++ b/packages/api/src/controllers/sessions.js @@ -188,7 +188,10 @@ module.exports = { }); logger.info({ sesid, sql }, 'DBGM-00019 Processing query'); - this.dispatchMessage(sesid, 'Query execution started'); + this.dispatchMessage(sesid, { + message: 'Query execution started', + sql, + }); session.subprocess.send({ msgtype: 'executeQuery', sql, diff --git a/packages/web/src/elements/SqlHighlighter.svelte b/packages/web/src/elements/SqlHighlighter.svelte index 41d3aa438..6f4e138c9 100644 --- a/packages/web/src/elements/SqlHighlighter.svelte +++ b/packages/web/src/elements/SqlHighlighter.svelte @@ -5,6 +5,8 @@ import { onMount, afterUpdate } from 'svelte'; export let code = ''; + export let inline = false; + export let onClick = null; let domCode; @@ -29,7 +31,11 @@ The `sql` class hints the language; highlight.js will read it even though we register the grammar explicitly. --> -
{code}
+ {#if inline} + {code} + {:else} +
{code}
+ {/if} {/key} diff --git a/packages/web/src/modals/ShowSqlModal.svelte b/packages/web/src/modals/ShowSqlModal.svelte new file mode 100644 index 000000000..8804ddc75 --- /dev/null +++ b/packages/web/src/modals/ShowSqlModal.svelte @@ -0,0 +1,53 @@ + + + +
SQL Script
+ +
+ +
+ +
+ + { + newQuery({ + initialData: sql, + }); + + closeCurrentModal(); + }} + data-testid="ShowSqlModal_openScriptButton" + /> +
+
+ + diff --git a/packages/web/src/plugins/ThemeDark.svelte b/packages/web/src/plugins/ThemeDark.svelte index 7afba20f4..d36e5a766 100644 --- a/packages/web/src/plugins/ThemeDark.svelte +++ b/packages/web/src/plugins/ThemeDark.svelte @@ -75,7 +75,7 @@ --token-base: #303030; --token-text: #ddd; - --token-keyword: white; + --token-keyword: #096dd9; --token-selector-tag: white; --token-literal: white; --token-section: white; diff --git a/packages/web/src/query/MessageView.svelte b/packages/web/src/query/MessageView.svelte index 465e53713..9a70182ed 100644 --- a/packages/web/src/query/MessageView.svelte +++ b/packages/web/src/query/MessageView.svelte @@ -12,6 +12,7 @@ export let startLine = 0; export let onMessageClick = null; export let onExplainError = null; + export let engine = null; export let filter = ''; @@ -92,6 +93,7 @@ previousRow={index > 0 ? items[index - 1] : null} {onMessageClick} {onExplainError} + {engine} /> {/each} diff --git a/packages/web/src/query/MessageViewRow.svelte b/packages/web/src/query/MessageViewRow.svelte index 4102e7425..129a680b5 100644 --- a/packages/web/src/query/MessageViewRow.svelte +++ b/packages/web/src/query/MessageViewRow.svelte @@ -17,6 +17,9 @@ import FontIcon from '../icons/FontIcon.svelte'; import { plusExpandIcon } from '../icons/expandIcons'; import InlineButton from '../buttons/InlineButton.svelte'; + import SqlHighlighter from '../elements/SqlHighlighter.svelte'; + import { showModal } from '../modals/modalTools'; + import ShowSqlModal from '../modals/ShowSqlModal.svelte'; export let row; export let index; @@ -29,6 +32,7 @@ export let previousRow = null; export let onMessageClick = null; export let onExplainError = null; + export let engine = null; let isExpanded = false; @@ -55,10 +59,22 @@ }}> Explain {/if} + {#if row.sql} + 100 ? '...' : '')} + inline + onClick={() => { + showModal(ShowSqlModal, { + sql: row.sql, + engine, + }); + }} + /> + {/if} {moment(row.time).format('HH:mm:ss')} {formatDuration(new Date(row.time).getTime() - time0)} - {previousRow ? formatDuration(new Date(row.time).getTime() - new Date(previousRow.time).getTime()) : 'n/a'} + {previousRow ? formatDuration(new Date(row.time).getTime() - new Date(previousRow.time).getTime()) : 'n/a'} {#if showProcedure} {row.procedure || ''} {/if} diff --git a/packages/web/src/query/SocketMessageView.svelte b/packages/web/src/query/SocketMessageView.svelte index 90e66f44f..808c74c0e 100644 --- a/packages/web/src/query/SocketMessageView.svelte +++ b/packages/web/src/query/SocketMessageView.svelte @@ -18,6 +18,7 @@ export let onChangeErrors = null; export let onMessageClick = null; export let onExplainError = null; + export let engine = null; const cachedMessagesRef = createRef([]); const lastErrorMessageCountRef = createRef(0); @@ -79,5 +80,6 @@ {showCaller} {startLine} {onExplainError} + {engine} /> {/if} diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index fc9b37d06..efbe32297 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -777,6 +777,7 @@ showLine onChangeErrors={handleChangeErrors} onExplainError={isProApp() ? handleExplainError : null} + engine={$connection && $connection.engine} />