diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 7b36da519..9d415701d 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -39,6 +39,8 @@ const axios = require('axios'); const { callTextToSqlApi, callCompleteOnCursorApi, callRefactorSqlQueryApi } = require('../utility/authProxy'); const { decryptConnection } = require('../utility/crypting'); const { getSshTunnel } = require('../utility/sshTunnel'); +const sessions = require('./sessions'); +const jsldata = require('./jsldata'); const logger = getLogger('databaseConnections'); @@ -96,6 +98,33 @@ module.exports = { handle_ping() {}, + // session event handlers + + handle_info(conid, database, props) { + const { sesid, info } = props; + sessions.dispatchMessage(sesid, info); + }, + + handle_done(conid, database, props) { + const { sesid } = props; + socket.emit(`session-done-${sesid}`); + sessions.dispatchMessage(sesid, 'Query execution finished'); + }, + + handle_recordset(conid, database, props) { + const { jslid, resultIndex } = props; + socket.emit(`session-recordset-${props.sesid}`, { jslid, resultIndex }); + }, + + handle_stats(conid, database, stats) { + jsldata.notifyChangedStats(stats); + }, + + handle_initializeFile(conid, database, props) { + const { jslid } = props; + socket.emit(`session-initialize-file-${jslid}`); + }, + async ensureOpened(conid, database) { const existing = this.opened.find(x => x.conid == conid && x.database == database); if (existing) return existing; @@ -763,4 +792,15 @@ module.exports = { commandLine: this.commandArgsToCommandLine(commandArgs), }; }, + + executeSessionQuery_meta: true, + async executeSessionQuery({ sesid, conid, database, sql }) { + logger.info({ sesid, sql }, 'Processing query'); + sessions.dispatchMessage(sesid, 'Query execution started'); + + const opened = await this.ensureOpened(conid, database); + opened.subprocess.send({ msgtype: 'executeSessionQuery', sql, sesid }); + + return { state: 'ok' }; + }, }; diff --git a/packages/api/src/proc/databaseConnectionProcess.js b/packages/api/src/proc/databaseConnectionProcess.js index e795af414..f75af1054 100644 --- a/packages/api/src/proc/databaseConnectionProcess.js +++ b/packages/api/src/proc/databaseConnectionProcess.js @@ -16,6 +16,7 @@ const { handleProcessCommunication } = require('../utility/processComm'); const { SqlGenerator } = require('dbgate-tools'); const generateDeploySql = require('../shell/generateDeploySql'); const { dumpSqlSelect } = require('dbgate-sqltree'); +const { allowExecuteCustomScript, handleQueryStream } = require('../utility/handleQueryStream'); const logger = getLogger('dbconnProcess'); @@ -375,6 +376,36 @@ async function handleGenerateDeploySql({ msgid, modelFolder }) { } } +async function handleExecuteSessionQuery({ sesid, sql }) { + await waitConnected(); + const driver = requireEngineDriver(storedConnection); + + if (!allowExecuteCustomScript(storedConnection, driver)) { + process.send({ + msgtype: 'info', + info: { + message: 'Connection without read-only sessions is read only', + severity: 'error', + }, + sesid, + }); + process.send({ msgtype: 'done', sesid, skipFinishedMessage: true }); + return; + //process.send({ msgtype: 'error', error: e.message }); + } + + const resultIndexHolder = { + value: 0, + }; + for (const sqlItem of splitQuery(sql, { + ...driver.getQuerySplitterOptions('stream'), + returnRichInfo: true, + })) { + await handleQueryStream(dbhan, driver, resultIndexHolder, sqlItem, sesid); + } + process.send({ msgtype: 'done', sesid }); +} + // async function handleRunCommand({ msgid, sql }) { // await waitConnected(); // const driver = engines(storedConnection); @@ -405,6 +436,7 @@ const messageHandlers = { sqlSelect: handleSqlSelect, exportKeys: handleExportKeys, schemaList: handleSchemaList, + executeSessionQuery: handleExecuteSessionQuery, // runCommand: handleRunCommand, }; diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 0caad1720..7c6c3b4a4 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -11,7 +11,7 @@ const { decryptConnection } = require('../utility/crypting'); const { connectUtility } = require('../utility/connectUtility'); const { handleProcessCommunication } = require('../utility/processComm'); const { getLogger, extractIntSettingsValue, extractBoolSettingsValue } = require('dbgate-tools'); -const { handleQueryStream, QueryStreamTableWriter } = require('../utility/handleQueryStream'); +const { handleQueryStream, QueryStreamTableWriter, allowExecuteCustomScript } = require('../utility/handleQueryStream'); const logger = getLogger('sessionProcess'); @@ -24,17 +24,6 @@ let lastActivity = null; let currentProfiler = null; let executingScripts = 0; -function allowExecuteCustomScript(driver) { - if (driver.readOnlySessions) { - return true; - } - if (storedConnection.isReadOnly) { - return false; - // throw new Error('Connection is read only'); - } - return true; -} - async function handleConnect(connection) { storedConnection = connection; @@ -65,7 +54,7 @@ async function handleStartProfiler({ jslid }) { await waitConnected(); const driver = requireEngineDriver(storedConnection); - if (!allowExecuteCustomScript(driver)) { + if (!allowExecuteCustomScript(storedConnection, driver)) { process.send({ msgtype: 'done' }); return; } @@ -94,7 +83,7 @@ async function handleExecuteControlCommand({ command }) { await waitConnected(); const driver = requireEngineDriver(storedConnection); - if (command == 'commitTransaction' && !allowExecuteCustomScript(driver)) { + if (command == 'commitTransaction' && !allowExecuteCustomScript(storedConnection, driver)) { process.send({ msgtype: 'info', info: { @@ -134,7 +123,7 @@ async function handleExecuteQuery({ sql, autoCommit }) { await waitConnected(); const driver = requireEngineDriver(storedConnection); - if (!allowExecuteCustomScript(driver)) { + if (!allowExecuteCustomScript(storedConnection, driver)) { process.send({ msgtype: 'info', info: { @@ -178,7 +167,7 @@ async function handleExecuteReader({ jslid, sql, fileName }) { if (fileName) { sql = fs.readFileSync(fileName, 'utf-8'); } else { - if (!allowExecuteCustomScript(driver)) { + if (!allowExecuteCustomScript(storedConnection, driver)) { process.send({ msgtype: 'done' }); return; } diff --git a/packages/api/src/utility/handleQueryStream.js b/packages/api/src/utility/handleQueryStream.js index 8201feebc..2ba193395 100644 --- a/packages/api/src/utility/handleQueryStream.js +++ b/packages/api/src/utility/handleQueryStream.js @@ -6,10 +6,11 @@ const _ = require('lodash'); const { jsldir } = require('../utility/directories'); class QueryStreamTableWriter { - constructor() { + constructor(sesid = undefined) { this.currentRowCount = 0; this.currentChangeIndex = 1; this.initializedFile = false; + this.sesid = sesid; } initializeFromQuery(structure, resultIndex) { @@ -26,7 +27,7 @@ class QueryStreamTableWriter { this.writeCurrentStats(false, false); this.resultIndex = resultIndex; this.initializedFile = true; - process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex }); + process.send({ msgtype: 'recordset', jslid: this.jslid, resultIndex, sesid: this.sesid }); } initializeFromReader(jslid) { @@ -52,7 +53,7 @@ class QueryStreamTableWriter { rowFromReader(row) { if (!this.initializedFile) { - process.send({ msgtype: 'initializeFile', jslid: this.jslid }); + process.send({ msgtype: 'initializeFile', jslid: this.jslid, sesid: this.sesid }); this.initializedFile = true; fs.writeFileSync(this.currentFile, JSON.stringify(row) + '\n'); @@ -75,7 +76,7 @@ class QueryStreamTableWriter { fs.writeFileSync(`${this.currentFile}.stats`, JSON.stringify(stats)); this.currentChangeIndex += 1; if (emitEvent) { - process.send({ msgtype: 'stats', ...stats }); + process.send({ msgtype: 'stats', sesid: this.sesid, ...stats }); } } @@ -90,9 +91,10 @@ class QueryStreamTableWriter { } class StreamHandler { - constructor(resultIndexHolder, resolve, startLine) { + constructor(resultIndexHolder, resolve, startLine, sesid = undefined) { this.recordset = this.recordset.bind(this); this.startLine = startLine; + this.sesid = sesid; this.row = this.row.bind(this); // this.error = this.error.bind(this); this.done = this.done.bind(this); @@ -116,7 +118,7 @@ class StreamHandler { recordset(columns) { this.closeCurrentWriter(); - this.currentWriter = new QueryStreamTableWriter(); + this.currentWriter = new QueryStreamTableWriter(this.sesid); this.currentWriter.initializeFromQuery( Array.isArray(columns) ? { columns } : columns, this.resultIndexHolder.value @@ -133,7 +135,7 @@ class StreamHandler { } row(row) { if (this.currentWriter) this.currentWriter.row(row); - else if (row.message) process.send({ msgtype: 'info', info: { message: row.message } }); + else if (row.message) process.send({ msgtype: 'info', info: { message: row.message }, sesid: this.sesid }); // this.onRow(this.jslid); } // error(error) { @@ -151,19 +153,31 @@ class StreamHandler { line: this.startLine + info.line, }; } - process.send({ msgtype: 'info', info }); + process.send({ msgtype: 'info', info, sesid: this.sesid }); } } -function handleQueryStream(dbhan, driver, resultIndexHolder, sqlItem) { +function handleQueryStream(dbhan, driver, resultIndexHolder, sqlItem, sesid = undefined) { return new Promise((resolve, reject) => { const start = sqlItem.trimStart || sqlItem.start; - const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line); + const handler = new StreamHandler(resultIndexHolder, resolve, start && start.line, sesid); driver.stream(dbhan, sqlItem.text, handler); }); } +function allowExecuteCustomScript(storedConnection, driver) { + if (driver.readOnlySessions) { + return true; + } + if (storedConnection.isReadOnly) { + return false; + // throw new Error('Connection is read only'); + } + return true; +} + module.exports = { handleQueryStream, QueryStreamTableWriter, + allowExecuteCustomScript, }; diff --git a/packages/web/src/query/SimpleQueryResultTabs.svelte b/packages/web/src/query/SimpleQueryResultTabs.svelte deleted file mode 100644 index 91b53f676..000000000 --- a/packages/web/src/query/SimpleQueryResultTabs.svelte +++ /dev/null @@ -1,52 +0,0 @@ - - - - - {#if grider && display} - - {/if} - - - {#if result?.errorMessage} - - {:else if result?.rows} - - {/if} - - diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index 1f05a94ff..992ccdddc 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -107,7 +107,7 @@