diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js
index 47321775f..8b731c37e 100644
--- a/packages/api/src/controllers/databaseConnections.js
+++ b/packages/api/src/controllers/databaseConnections.js
@@ -182,6 +182,15 @@ module.exports = {
return res;
},
+ runOperation_meta: true,
+ async runOperation({ conid, database, operation, useTransaction }, req) {
+ testConnectionPermission(conid, req);
+ logger.info({ conid, database, operation }, 'Processing operation');
+ const opened = await this.ensureOpened(conid, database);
+ const res = await this.sendRequest(opened, { msgtype: 'runOperation', operation, useTransaction });
+ return res;
+ },
+
collectionData_meta: true,
async collectionData({ conid, database, options }, req) {
testConnectionPermission(conid, req);
diff --git a/packages/api/src/proc/databaseConnectionProcess.js b/packages/api/src/proc/databaseConnectionProcess.js
index 9a8756431..894fba18c 100644
--- a/packages/api/src/proc/databaseConnectionProcess.js
+++ b/packages/api/src/proc/databaseConnectionProcess.js
@@ -170,6 +170,18 @@ async function handleRunScript({ msgid, sql, useTransaction }, skipReadonlyCheck
}
}
+async function handleRunOperation({ msgid, operation, useTransaction }, skipReadonlyCheck = false) {
+ await waitConnected();
+ const driver = requireEngineDriver(storedConnection);
+ try {
+ if (!skipReadonlyCheck) ensureExecuteCustomScript(driver);
+ await driver.operation(systemConnection, operation, { useTransaction });
+ process.send({ msgtype: 'response', msgid });
+ } catch (err) {
+ process.send({ msgtype: 'response', msgid, errorMessage: err.message });
+ }
+}
+
async function handleQueryData({ msgid, sql }, skipReadonlyCheck = false) {
await waitConnected();
const driver = requireEngineDriver(storedConnection);
@@ -311,6 +323,7 @@ const messageHandlers = {
connect: handleConnect,
queryData: handleQueryData,
runScript: handleRunScript,
+ runOperation: handleRunOperation,
updateCollection: handleUpdateCollection,
collectionData: handleCollectionData,
loadKeys: handleLoadKeys,
diff --git a/packages/tools/src/driverBase.ts b/packages/tools/src/driverBase.ts
index 20044d7a5..e3ea86f91 100644
--- a/packages/tools/src/driverBase.ts
+++ b/packages/tools/src/driverBase.ts
@@ -81,6 +81,9 @@ export const driverBase = {
runCommandOnDriver(pool, this, dmp => dmp.commitTransaction());
}
},
+ async operation(pool, operation, options: RunScriptOptions) {
+ throw new Error('Operation not defined in target driver');
+ },
getNewObjectTemplates() {
if (this.databaseEngineTypes.includes('sql')) {
return [{ label: 'New view', sql: 'CREATE VIEW myview\nAS\nSELECT * FROM table1' }];
diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts
index 4262a4c33..b2f3afea6 100644
--- a/packages/types/engines.d.ts
+++ b/packages/types/engines.d.ts
@@ -142,6 +142,7 @@ export interface EngineDriver {
dropDatabase(pool: any, name: string): Promise;
getQuerySplitterOptions(usage: 'stream' | 'script' | 'editor'): any;
script(pool: any, sql: string, options?: RunScriptOptions): Promise;
+ operation(pool: any, operation: {}, options?: RunScriptOptions): Promise;
getNewObjectTemplates(): NewObjectTemplate[];
// direct call of pool method, only some methods could be supported, on only some drivers
callMethod(pool, method, args);
diff --git a/packages/web/src/appobj/DatabaseAppObject.svelte b/packages/web/src/appobj/DatabaseAppObject.svelte
index 21c1ce657..57049e89f 100644
--- a/packages/web/src/appobj/DatabaseAppObject.svelte
+++ b/packages/web/src/appobj/DatabaseAppObject.svelte
@@ -1,5 +1,5 @@