diff --git a/packages/api/src/controllers/sessions.js b/packages/api/src/controllers/sessions.js index a0a5b66e6..bb5338699 100644 --- a/packages/api/src/controllers/sessions.js +++ b/packages/api/src/controllers/sessions.js @@ -56,7 +56,11 @@ module.exports = { handle_done(sesid, props) { socket.emit(`session-done-${sesid}`); if (!props.skipFinishedMessage) { - this.dispatchMessage(sesid, 'Query execution finished'); + if (props.controlCommand) { + this.dispatchMessage(sesid, `${_.startCase(props.controlCommand)} finished`); + } else { + this.dispatchMessage(sesid, 'Query execution finished'); + } } const session = this.opened.find(x => x.sesid == sesid); if (session.loadingReader_jslid) { @@ -144,6 +148,20 @@ module.exports = { return { state: 'ok' }; }, + executeControlCommand_meta: true, + async executeControlCommand({ sesid, command }) { + const session = this.opened.find(x => x.sesid == sesid); + if (!session) { + throw new Error('Invalid session'); + } + + logger.info({ sesid, command }, 'Processing control command'); + this.dispatchMessage(sesid, `${_.startCase(command)} started`); + session.subprocess.send({ msgtype: 'executeControlCommand', command }); + + return { state: 'ok' }; + }, + executeReader_meta: true, async executeReader({ conid, database, sql, queryName, appFolder }) { const { sesid } = await this.create({ conid, database }); diff --git a/packages/api/src/proc/sessionProcess.js b/packages/api/src/proc/sessionProcess.js index 176c24dbe..182f422b7 100644 --- a/packages/api/src/proc/sessionProcess.js +++ b/packages/api/src/proc/sessionProcess.js @@ -245,6 +245,46 @@ async function handleStopProfiler({ jslid }) { currentProfiler = null; } +async function handleExecuteControlCommand({ command }) { + lastActivity = new Date().getTime(); + + await waitConnected(); + const driver = requireEngineDriver(storedConnection); + + if (command == 'commitTransaction' && !allowExecuteCustomScript(driver)) { + process.send({ + msgtype: 'info', + info: { + message: 'Connection without read-only sessions is read only', + severity: 'error', + }, + }); + process.send({ msgtype: 'done', skipFinishedMessage: true }); + return; + //process.send({ msgtype: 'error', error: e.message }); + } + + executingScripts++; + try { + const dmp = driver.createDumper(); + switch (command) { + case 'commitTransaction': + await dmp.commitTransaction(); + break; + case 'rollbackTransaction': + await dmp.rollbackTransaction(); + break; + case 'beginTransaction': + await dmp.beginTransaction(); + break; + } + await driver.query(dbhan, dmp.s, { discardResult: true }); + process.send({ msgtype: 'done', controlCommand: command }); + } finally { + executingScripts--; + } +} + async function handleExecuteQuery({ sql }) { lastActivity = new Date().getTime(); @@ -323,6 +363,7 @@ function handlePing() { const messageHandlers = { connect: handleConnect, executeQuery: handleExecuteQuery, + executeControlCommand: handleExecuteControlCommand, executeReader: handleExecuteReader, startProfiler: handleStartProfiler, stopProfiler: handleStopProfiler, diff --git a/packages/web/src/tabs/QueryTab.svelte b/packages/web/src/tabs/QueryTab.svelte index d7709c213..2617207d6 100644 --- a/packages/web/src/tabs/QueryTab.svelte +++ b/packages/web/src/tabs/QueryTab.svelte @@ -353,6 +353,26 @@ }); } + async function executeControlCommand(command) { + busy = true; + timerLabel.start(); + visibleResultTabs = true; + + let sesid = sessionId; + if (!sesid) { + const resp = await apiCall('sessions/create', { + conid, + database, + }); + sesid = resp.sesid; + sessionId = sesid; + } + await apiCall('sessions/execute-control-command', { + sesid, + command, + }); + } + export async function executeCurrent() { const cmd = domEditor.getCurrentCommandText(); await executeCore(cmd.text, cmd.line); @@ -426,10 +446,8 @@ } export function beginTransaction() { - const dmp = driver.createDumper(); - dmp.beginTransaction(); - executeCore(dmp.s); isInTransaction = true; + executeControlCommand('beginTransaction'); } export function beginTransactionEnabled() { @@ -457,17 +475,13 @@ } export function commitTransaction() { - const dmp = driver.createDumper(); - dmp.commitTransaction(); - executeCore(dmp.s); isInTransaction = false; + executeControlCommand('commitTransaction'); } export function rollbackTransaction() { - const dmp = driver.createDumper(); - dmp.rollbackTransaction(); - executeCore(dmp.s); isInTransaction = false; + executeControlCommand('rollbackTransaction'); } const handleMesageClick = message => {