diff --git a/packages/api/src/controllers/serverConnections.js b/packages/api/src/controllers/serverConnections.js index 7f6ad2b6d..5b06d3078 100644 --- a/packages/api/src/controllers/serverConnections.js +++ b/packages/api/src/controllers/serverConnections.js @@ -152,4 +152,13 @@ module.exports = { opened.subprocess.send({ msgtype: 'createDatabase', name }); return { status: 'ok' }; }, + + dropDatabase_meta: true, + async dropDatabase({ conid, name }, req) { + testConnectionPermission(conid, req); + const opened = await this.ensureOpened(conid); + if (opened.connection.isReadOnly) return false; + opened.subprocess.send({ msgtype: 'dropDatabase', name }); + return { status: 'ok' }; + }, }; diff --git a/packages/api/src/proc/serverConnectionProcess.js b/packages/api/src/proc/serverConnectionProcess.js index 42e5cb8d9..592071040 100644 --- a/packages/api/src/proc/serverConnectionProcess.js +++ b/packages/api/src/proc/serverConnectionProcess.js @@ -2,7 +2,6 @@ const stableStringify = require('json-stable-stringify'); const { extractBoolSettingsValue, extractIntSettingsValue } = require('dbgate-tools'); const childProcessChecker = require('../utility/childProcessChecker'); const requireEngineDriver = require('../utility/requireEngineDriver'); -const { decryptConnection } = require('../utility/crypting'); const connectUtility = require('../utility/connectUtility'); const { handleProcessCommunication } = require('../utility/processComm'); @@ -81,14 +80,16 @@ function handlePing() { lastPing = new Date().getTime(); } -async function handleCreateDatabase({ name }) { +async function handleDatabaseOp(op, { name }) { const driver = requireEngineDriver(storedConnection); systemConnection = await connectUtility(driver, storedConnection, 'app'); - console.log(`RUNNING SCRIPT: CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`); - if (driver.createDatabase) { - await driver.createDatabase(systemConnection, name); + if (driver[op]) { + await driver[op](systemConnection, name); } else { - await driver.query(systemConnection, `CREATE DATABASE ${driver.dialect.quoteIdentifier(name)}`); + const dmp = driver.createDumper(); + dmp[op](name); + console.log(`RUNNING SCRIPT: ${dmp.s}`); + await driver.query(systemConnection, dmp.s); } await handleRefresh(); } @@ -96,7 +97,8 @@ async function handleCreateDatabase({ name }) { const messageHandlers = { connect: handleConnect, ping: handlePing, - createDatabase: handleCreateDatabase, + createDatabase: props => handleDatabaseOp('createDatabase', props), + dropDatabase: props => handleDatabaseOp('dropDatabase', props), }; async function handleMessage({ msgtype, ...other }) { diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts index 063989a3f..3a6de60c7 100644 --- a/packages/tools/src/SqlDumper.ts +++ b/packages/tools/src/SqlDumper.ts @@ -181,6 +181,14 @@ export class SqlDumper implements AlterProcessor { this.put(' ^auto_increment'); } + createDatabase(name: string) { + this.putCmd('^create ^database %i', name); + } + + dropDatabase(name: string) { + this.putCmd('^drop ^database %i', name); + } + specialColumnOptions(column) {} columnDefinition(column: ColumnInfo, { includeDefault = true, includeNullable = true, includeCollate = true } = {}) { diff --git a/packages/types/dumper.d.ts b/packages/types/dumper.d.ts index 9d35fbdee..8c3f0147b 100644 --- a/packages/types/dumper.d.ts +++ b/packages/types/dumper.d.ts @@ -14,6 +14,8 @@ export interface SqlDumper extends AlterProcessor { putValue(value: string | number | Date); putCollection(delimiter: string, collection: T[], lambda: (item: T) => void); transform(type: TransformType, dumpExpr: () => void); + createDatabase(name: string); + dropDatabase(name: string); endCommand(); allowIdentityInsert(table: NamedObjectInfo, allow: boolean); diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 39ac92043..6673b64a9 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -110,6 +110,7 @@ export interface EngineDriver { updateCollection(pool: any, changeSet: any): Promise; getCollectionUpdateScript(changeSet: any): string; createDatabase(pool: any, name: string): Promise; + dropDatabase(pool: any, name: string): Promise; getQuerySplitterOptions(usage: 'stream' | 'script' | 'editor'): any; script(pool: any, sql: string): Promise; getNewObjectTemplates(): NewObjectTemplate[]; diff --git a/packages/web/src/appobj/DatabaseAppObject.svelte b/packages/web/src/appobj/DatabaseAppObject.svelte index 643751937..fba48176c 100644 --- a/packages/web/src/appobj/DatabaseAppObject.svelte +++ b/packages/web/src/appobj/DatabaseAppObject.svelte @@ -77,6 +77,17 @@ ); }; + const handleDropDatabase = () => { + showModal(ConfirmModal, { + message: `Really drop database ${name}? All opened sessions with this database will be forcefully closed.`, + onConfirm: () => + apiCall('server-connections/drop-database', { + conid: connection._id, + name, + }), + }); + }; + const handleNewCollection = () => { showModal(InputTextModal, { value: '', @@ -233,6 +244,9 @@ { onClick: handleNewQuery, text: 'New query', isNewQuery: true }, driver?.databaseEngineTypes?.includes('sql') && { onClick: handleNewTable, text: 'New table' }, driver?.databaseEngineTypes?.includes('document') && { onClick: handleNewCollection, text: 'New collection' }, + isSqlOrDoc && + !connection.isReadOnly && + !connection.singleDatabase && { onClick: handleDropDatabase, text: 'Drop database' }, { divider: true }, isSqlOrDoc && !connection.isReadOnly && { onClick: handleImport, text: 'Import wizard' }, isSqlOrDoc && { onClick: handleExport, text: 'Export wizard' }, diff --git a/plugins/dbgate-plugin-mongo/src/backend/driver.js b/plugins/dbgate-plugin-mongo/src/backend/driver.js index ece65e41a..756c6a5a1 100644 --- a/plugins/dbgate-plugin-mongo/src/backend/driver.js +++ b/plugins/dbgate-plugin-mongo/src/backend/driver.js @@ -276,6 +276,11 @@ const driver = { await db.createCollection('collection1'); }, + async dropDatabase(pool, name) { + const db = pool.db(name); + await db.dropDatabase(); + }, + async loadFieldValues(pool, name, field, search) { try { const collection = pool.__getDatabase().collection(name.pureName); diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js index f83cba1e8..d308b2eed 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -16,6 +16,16 @@ class MsSqlDumper extends SqlDumper { } } + dropDatabase(name) { + this.putCmd( + `USE master; + ALTER DATABASE %i SET SINGLE_USER WITH ROLLBACK IMMEDIATE; + DROP DATABASE %i`, + name, + name + ); + } + autoIncrement() { this.put(' ^identity'); } diff --git a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js index d47aa78d2..363193ef7 100644 --- a/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js +++ b/plugins/dbgate-plugin-postgres/src/frontend/Dumper.js @@ -26,6 +26,10 @@ class Dumper extends SqlDumper { } } + dropDatabase(name) { + this.putCmd('^drop ^database %i ^with(^force)', name); + } + dropRecreatedTempTable(tmptable) { this.putCmd('^drop ^table %i ^cascade', tmptable); }