diff --git a/packages/tools/src/DatabaseAnalyser.ts b/packages/tools/src/DatabaseAnalyser.ts
index 255de678..b189a390 100644
--- a/packages/tools/src/DatabaseAnalyser.ts
+++ b/packages/tools/src/DatabaseAnalyser.ts
@@ -5,6 +5,7 @@ import _pick from 'lodash/pick';
import _compact from 'lodash/compact';
import { getLogger } from './getLogger';
import { type Logger } from 'pinomin';
+import stableStringify from 'json-stable-stringify';
const logger = getLogger('dbAnalyser');
@@ -70,7 +71,10 @@ export class DatabaseAnalyser {
async fullAnalysis() {
const res = this.addEngineField(await this._runAnalysis());
// console.log('FULL ANALYSIS', res);
- return res;
+ return {
+ ...res,
+ schemas: await this.readSchemaList(),
+ };
}
async singleObjectAnalysis(name, typeField) {
@@ -87,6 +91,10 @@ export class DatabaseAnalyser {
return obj;
}
+ async readSchemaList() {
+ return undefined;
+ }
+
async incrementalAnalysis(structure) {
this.structure = structure;
@@ -99,20 +107,29 @@ export class DatabaseAnalyser {
const structureModifications = modifications.filter(x => x.action != 'setTableRowCounts');
const setTableRowCounts = modifications.find(x => x.action == 'setTableRowCounts');
- let structureWithRowCounts = null;
+ let structureUpdated = null;
if (setTableRowCounts) {
const newStructure = mergeTableRowCounts(structure, setTableRowCounts.rowCounts);
if (areDifferentRowCounts(structure, newStructure)) {
- structureWithRowCounts = newStructure;
+ structureUpdated = newStructure;
}
}
+ const schemas = await this.readSchemaList();
+ const areSchemasDifferent = stableStringify(schemas) != stableStringify(this.structure.schemas);
+ if (areSchemasDifferent) {
+ structureUpdated = {
+ ...structureUpdated,
+ schemas,
+ };
+ }
+
if (structureModifications.length == 0) {
- return structureWithRowCounts ? this.addEngineField(structureWithRowCounts) : null;
+ return structureUpdated ? this.addEngineField(structureUpdated) : null;
}
this.modifications = structureModifications;
- if (structureWithRowCounts) this.structure = structureWithRowCounts;
+ if (structureUpdated) this.structure = structureUpdated;
logger.info({ modifications: this.modifications }, 'DB modifications detected:');
return this.addEngineField(this.mergeAnalyseResult(await this._runAnalysis()));
}
diff --git a/packages/tools/src/SqlDumper.ts b/packages/tools/src/SqlDumper.ts
index 4e0582d3..3fe9e625 100644
--- a/packages/tools/src/SqlDumper.ts
+++ b/packages/tools/src/SqlDumper.ts
@@ -214,6 +214,14 @@ export class SqlDumper implements AlterProcessor {
this.putCmd('^drop ^database %i', name);
}
+ createSchema(name: string) {
+ this.putCmd('^create ^schema %i', name);
+ }
+
+ dropSchema(name: string) {
+ this.putCmd('^drop ^schema %i', name);
+ }
+
specialColumnOptions(column) {}
selectScopeIdentity(table: TableInfo) {}
diff --git a/packages/tools/src/driverBase.ts b/packages/tools/src/driverBase.ts
index ebcf03cd..251ea87b 100644
--- a/packages/tools/src/driverBase.ts
+++ b/packages/tools/src/driverBase.ts
@@ -84,7 +84,17 @@ export const driverBase = {
}
},
async operation(pool, operation, options: RunScriptOptions) {
- throw new Error('Operation not defined in target driver');
+ const { type } = operation;
+ switch (type) {
+ case 'createSchema':
+ await runCommandOnDriver(pool, this, dmp => dmp.createSchema(operation.schemaName));
+ break;
+ case 'dropSchema':
+ await runCommandOnDriver(pool, this, dmp => dmp.dropSchema(operation.schemaName));
+ break;
+ default:
+ throw new Error(`Operation type ${type} not supported`);
+ }
},
getNewObjectTemplates() {
if (this.databaseEngineTypes.includes('sql')) {
@@ -180,5 +190,5 @@ export const driverBase = {
adaptTableInfo(table) {
return table;
- }
+ },
};
diff --git a/packages/web/src/widgets/SchemaSelector.svelte b/packages/web/src/widgets/SchemaSelector.svelte
index ffdc1a3e..bfb457e0 100644
--- a/packages/web/src/widgets/SchemaSelector.svelte
+++ b/packages/web/src/widgets/SchemaSelector.svelte
@@ -5,6 +5,10 @@
import _ from 'lodash';
import FontIcon from '../icons/FontIcon.svelte';
import { DatabaseInfo } from 'dbgate-types';
+ import { showModal } from '../modals/modalTools';
+ import ConfirmModal from '../modals/ConfirmModal.svelte';
+ import { runOperationOnDatabase } from '../modals/ConfirmSqlModal.svelte';
+ import InputTextModal from '../modals/InputTextModal.svelte';
export let dbinfo: DatabaseInfo;
export let selectedSchema;
@@ -13,6 +17,9 @@
export let onApplySelectedSchema;
export let valueStorageKey;
+ export let conid;
+ export let database;
+
let appliedSchema;
$: {
@@ -45,8 +52,33 @@
);
$: countBySchema = computeCountBySchema(objectList ?? []);
- function handleAddNewSchema() {
- // runCommand('add-schema', { conid: dbinfo.conid, database: dbinfo.database });
+ function handleCreateSchema() {
+ showModal(InputTextModal, {
+ header: 'Create schema',
+ value: 'newschema',
+ label: 'Schema name',
+ onConfirm: async name => {
+ const dbid = { conid, database };
+ await runOperationOnDatabase(dbid, {
+ type: 'createSchema',
+ schemaName: name,
+ });
+ selectedSchema = name;
+ },
+ });
+ }
+ function handleDropSchema() {
+ showModal(ConfirmModal, {
+ message: `Really drop schema ${appliedSchema}?`,
+ onConfirm: async () => {
+ const dbid = { conid, database };
+ runOperationOnDatabase(dbid, {
+ type: 'dropSchema',
+ schemaName: appliedSchema,
+ });
+ selectedSchema = null;
+ },
+ });
}
$: selectedSchema = localStorage.getItem(valueStorageKey ?? '');
@@ -81,10 +113,10 @@
{/if}
-
+
-
+
diff --git a/packages/web/src/widgets/SqlObjectList.svelte b/packages/web/src/widgets/SqlObjectList.svelte
index ce6c3475..7c2ec45f 100644
--- a/packages/web/src/widgets/SqlObjectList.svelte
+++ b/packages/web/src/widgets/SqlObjectList.svelte
@@ -151,6 +151,8 @@
appliedSelectedSchema = x;
}}
valueStorageKey={`sql-object-list-schema-${conid}-${database}`}
+ {conid}
+ {database}
/>
diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js
index 3942b03c..75793391 100644
--- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js
+++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js
@@ -79,6 +79,12 @@ class MsSqlAnalyser extends DatabaseAnalyser {
this.singleObjectId = resId.rows[0].id;
}
+ async readSchemaList() {
+ const schemaRows = await this.analyserQuery('getSchemas');
+ const schemas = schemaRows.rows;
+ return schemas;
+ }
+
async _runAnalysis() {
this.feedback({ analysingMessage: 'Loading tables' });
const tablesRows = await this.analyserQuery('tables', ['tables']);
@@ -88,8 +94,6 @@ class MsSqlAnalyser extends DatabaseAnalyser {
const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']);
this.feedback({ analysingMessage: 'Loading foreign keys' });
const fkColumnsRows = await this.analyserQuery('foreignKeys', ['tables']);
- this.feedback({ analysingMessage: 'Loading schemas' });
- const schemaRows = await this.analyserQuery('getSchemas');
this.feedback({ analysingMessage: 'Loading indexes' });
const indexesRows = await this.analyserQuery('indexes', ['tables']);
this.feedback({ analysingMessage: 'Loading index columns' });
@@ -99,17 +103,10 @@ class MsSqlAnalyser extends DatabaseAnalyser {
this.feedback({ analysingMessage: 'Loading table sizes' });
const tableSizes = await this.analyserQuery('tableSizes');
- const schemas = schemaRows.rows;
-
const tableSizesDict = _.mapValues(_.keyBy(tableSizes.rows, 'objectId'), 'tableRowCount');
this.feedback({ analysingMessage: 'Loading SQL code' });
- const sqlCodeRows = await this.analyserQuery('loadSqlCode', [
- 'views',
- 'procedures',
- 'functions',
- 'triggers',
- ]);
+ const sqlCodeRows = await this.analyserQuery('loadSqlCode', ['views', 'procedures', 'functions', 'triggers']);
const getCreateSql = row =>
sqlCodeRows.rows
.filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName)
@@ -182,7 +179,6 @@ class MsSqlAnalyser extends DatabaseAnalyser {
views,
procedures,
functions,
- schemas,
defaultSchema: defaultSchemaRows.rows[0] ? defaultSchemaRows.rows[0].name : undefined,
};
}
diff --git a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js
index f5153af8..bca81994 100644
--- a/plugins/dbgate-plugin-postgres/src/backend/Analyser.js
+++ b/plugins/dbgate-plugin-postgres/src/backend/Analyser.js
@@ -312,6 +312,17 @@ class Analyser extends DatabaseAnalyser {
return res;
}
+ async readSchemaList() {
+ const schemaRows = await this.analyserQuery('getSchemas');
+
+ const schemas = schemaRows.rows.map(x => ({
+ schemaName: x.schema_name,
+ objectId: `schemas:${x.schema_name}`,
+ }));
+
+ return schemas;
+ }
+
async _getFastSnapshot() {
const tableModificationsQueryData = this.driver.dialect.stringAgg
? await this.analyserQuery('tableModifications')
diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/getSchemas.js b/plugins/dbgate-plugin-postgres/src/backend/sql/getSchemas.js
new file mode 100644
index 00000000..88f8972d
--- /dev/null
+++ b/plugins/dbgate-plugin-postgres/src/backend/sql/getSchemas.js
@@ -0,0 +1 @@
+module.exports = `select oid as "object_id", nspname as "schema_name" from pg_catalog.pg_namespace`;
diff --git a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js
index 53a858ab..9fb45a93 100644
--- a/plugins/dbgate-plugin-postgres/src/backend/sql/index.js
+++ b/plugins/dbgate-plugin-postgres/src/backend/sql/index.js
@@ -12,6 +12,7 @@ const matviewColumns = require('./matviewColumns');
const indexes = require('./indexes');
const indexcols = require('./indexcols');
const uniqueNames = require('./uniqueNames');
+const getSchemas = require('./getSchemas');
const geometryColumns = require('./geometryColumns');
const geographyColumns = require('./geographyColumns');
@@ -39,4 +40,5 @@ module.exports = {
uniqueNames,
geometryColumns,
geographyColumns,
+ getSchemas,
};