mirror of
https://github.com/dbgate/dbgate
synced 2024-11-13 23:33:47 +00:00
schema update in database analyser
This commit is contained in:
parent
327d43096f
commit
5ab686b721
@ -5,6 +5,7 @@ import _pick from 'lodash/pick';
|
|||||||
import _compact from 'lodash/compact';
|
import _compact from 'lodash/compact';
|
||||||
import { getLogger } from './getLogger';
|
import { getLogger } from './getLogger';
|
||||||
import { type Logger } from 'pinomin';
|
import { type Logger } from 'pinomin';
|
||||||
|
import stableStringify from 'json-stable-stringify';
|
||||||
|
|
||||||
const logger = getLogger('dbAnalyser');
|
const logger = getLogger('dbAnalyser');
|
||||||
|
|
||||||
@ -70,7 +71,10 @@ export class DatabaseAnalyser {
|
|||||||
async fullAnalysis() {
|
async fullAnalysis() {
|
||||||
const res = this.addEngineField(await this._runAnalysis());
|
const res = this.addEngineField(await this._runAnalysis());
|
||||||
// console.log('FULL ANALYSIS', res);
|
// console.log('FULL ANALYSIS', res);
|
||||||
return res;
|
return {
|
||||||
|
...res,
|
||||||
|
schemas: await this.readSchemaList(),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async singleObjectAnalysis(name, typeField) {
|
async singleObjectAnalysis(name, typeField) {
|
||||||
@ -87,6 +91,10 @@ export class DatabaseAnalyser {
|
|||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async readSchemaList() {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
async incrementalAnalysis(structure) {
|
async incrementalAnalysis(structure) {
|
||||||
this.structure = structure;
|
this.structure = structure;
|
||||||
|
|
||||||
@ -99,20 +107,29 @@ export class DatabaseAnalyser {
|
|||||||
const structureModifications = modifications.filter(x => x.action != 'setTableRowCounts');
|
const structureModifications = modifications.filter(x => x.action != 'setTableRowCounts');
|
||||||
const setTableRowCounts = modifications.find(x => x.action == 'setTableRowCounts');
|
const setTableRowCounts = modifications.find(x => x.action == 'setTableRowCounts');
|
||||||
|
|
||||||
let structureWithRowCounts = null;
|
let structureUpdated = null;
|
||||||
if (setTableRowCounts) {
|
if (setTableRowCounts) {
|
||||||
const newStructure = mergeTableRowCounts(structure, setTableRowCounts.rowCounts);
|
const newStructure = mergeTableRowCounts(structure, setTableRowCounts.rowCounts);
|
||||||
if (areDifferentRowCounts(structure, newStructure)) {
|
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) {
|
if (structureModifications.length == 0) {
|
||||||
return structureWithRowCounts ? this.addEngineField(structureWithRowCounts) : null;
|
return structureUpdated ? this.addEngineField(structureUpdated) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.modifications = structureModifications;
|
this.modifications = structureModifications;
|
||||||
if (structureWithRowCounts) this.structure = structureWithRowCounts;
|
if (structureUpdated) this.structure = structureUpdated;
|
||||||
logger.info({ modifications: this.modifications }, 'DB modifications detected:');
|
logger.info({ modifications: this.modifications }, 'DB modifications detected:');
|
||||||
return this.addEngineField(this.mergeAnalyseResult(await this._runAnalysis()));
|
return this.addEngineField(this.mergeAnalyseResult(await this._runAnalysis()));
|
||||||
}
|
}
|
||||||
|
@ -214,6 +214,14 @@ export class SqlDumper implements AlterProcessor {
|
|||||||
this.putCmd('^drop ^database %i', name);
|
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) {}
|
specialColumnOptions(column) {}
|
||||||
|
|
||||||
selectScopeIdentity(table: TableInfo) {}
|
selectScopeIdentity(table: TableInfo) {}
|
||||||
|
@ -84,7 +84,17 @@ export const driverBase = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async operation(pool, operation, options: RunScriptOptions) {
|
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() {
|
getNewObjectTemplates() {
|
||||||
if (this.databaseEngineTypes.includes('sql')) {
|
if (this.databaseEngineTypes.includes('sql')) {
|
||||||
@ -180,5 +190,5 @@ export const driverBase = {
|
|||||||
|
|
||||||
adaptTableInfo(table) {
|
adaptTableInfo(table) {
|
||||||
return table;
|
return table;
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import FontIcon from '../icons/FontIcon.svelte';
|
import FontIcon from '../icons/FontIcon.svelte';
|
||||||
import { DatabaseInfo } from 'dbgate-types';
|
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 dbinfo: DatabaseInfo;
|
||||||
export let selectedSchema;
|
export let selectedSchema;
|
||||||
@ -13,6 +17,9 @@
|
|||||||
export let onApplySelectedSchema;
|
export let onApplySelectedSchema;
|
||||||
export let valueStorageKey;
|
export let valueStorageKey;
|
||||||
|
|
||||||
|
export let conid;
|
||||||
|
export let database;
|
||||||
|
|
||||||
let appliedSchema;
|
let appliedSchema;
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
@ -45,8 +52,33 @@
|
|||||||
);
|
);
|
||||||
$: countBySchema = computeCountBySchema(objectList ?? []);
|
$: countBySchema = computeCountBySchema(objectList ?? []);
|
||||||
|
|
||||||
function handleAddNewSchema() {
|
function handleCreateSchema() {
|
||||||
// runCommand('add-schema', { conid: dbinfo.conid, database: dbinfo.database });
|
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 ?? '');
|
$: selectedSchema = localStorage.getItem(valueStorageKey ?? '');
|
||||||
@ -81,10 +113,10 @@
|
|||||||
<FontIcon icon="icon close" />
|
<FontIcon icon="icon close" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
{/if}
|
{/if}
|
||||||
<InlineButton on:click={handleAddNewSchema} title="Add new schema" square>
|
<InlineButton on:click={handleCreateSchema} title="Add new schema" square>
|
||||||
<FontIcon icon="icon plus-thick" />
|
<FontIcon icon="icon plus-thick" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
<InlineButton on:click={handleAddNewSchema} title="Delete schema" square>
|
<InlineButton on:click={handleDropSchema} title="Delete schema" square disabled={!appliedSchema}>
|
||||||
<FontIcon icon="icon minus-thick" />
|
<FontIcon icon="icon minus-thick" />
|
||||||
</InlineButton>
|
</InlineButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -151,6 +151,8 @@
|
|||||||
appliedSelectedSchema = x;
|
appliedSelectedSchema = x;
|
||||||
}}
|
}}
|
||||||
valueStorageKey={`sql-object-list-schema-${conid}-${database}`}
|
valueStorageKey={`sql-object-list-schema-${conid}-${database}`}
|
||||||
|
{conid}
|
||||||
|
{database}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<WidgetsInnerContainer>
|
<WidgetsInnerContainer>
|
||||||
|
@ -79,6 +79,12 @@ class MsSqlAnalyser extends DatabaseAnalyser {
|
|||||||
this.singleObjectId = resId.rows[0].id;
|
this.singleObjectId = resId.rows[0].id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async readSchemaList() {
|
||||||
|
const schemaRows = await this.analyserQuery('getSchemas');
|
||||||
|
const schemas = schemaRows.rows;
|
||||||
|
return schemas;
|
||||||
|
}
|
||||||
|
|
||||||
async _runAnalysis() {
|
async _runAnalysis() {
|
||||||
this.feedback({ analysingMessage: 'Loading tables' });
|
this.feedback({ analysingMessage: 'Loading tables' });
|
||||||
const tablesRows = await this.analyserQuery('tables', ['tables']);
|
const tablesRows = await this.analyserQuery('tables', ['tables']);
|
||||||
@ -88,8 +94,6 @@ class MsSqlAnalyser extends DatabaseAnalyser {
|
|||||||
const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']);
|
const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']);
|
||||||
this.feedback({ analysingMessage: 'Loading foreign keys' });
|
this.feedback({ analysingMessage: 'Loading foreign keys' });
|
||||||
const fkColumnsRows = await this.analyserQuery('foreignKeys', ['tables']);
|
const fkColumnsRows = await this.analyserQuery('foreignKeys', ['tables']);
|
||||||
this.feedback({ analysingMessage: 'Loading schemas' });
|
|
||||||
const schemaRows = await this.analyserQuery('getSchemas');
|
|
||||||
this.feedback({ analysingMessage: 'Loading indexes' });
|
this.feedback({ analysingMessage: 'Loading indexes' });
|
||||||
const indexesRows = await this.analyserQuery('indexes', ['tables']);
|
const indexesRows = await this.analyserQuery('indexes', ['tables']);
|
||||||
this.feedback({ analysingMessage: 'Loading index columns' });
|
this.feedback({ analysingMessage: 'Loading index columns' });
|
||||||
@ -99,17 +103,10 @@ class MsSqlAnalyser extends DatabaseAnalyser {
|
|||||||
this.feedback({ analysingMessage: 'Loading table sizes' });
|
this.feedback({ analysingMessage: 'Loading table sizes' });
|
||||||
const tableSizes = await this.analyserQuery('tableSizes');
|
const tableSizes = await this.analyserQuery('tableSizes');
|
||||||
|
|
||||||
const schemas = schemaRows.rows;
|
|
||||||
|
|
||||||
const tableSizesDict = _.mapValues(_.keyBy(tableSizes.rows, 'objectId'), 'tableRowCount');
|
const tableSizesDict = _.mapValues(_.keyBy(tableSizes.rows, 'objectId'), 'tableRowCount');
|
||||||
|
|
||||||
this.feedback({ analysingMessage: 'Loading SQL code' });
|
this.feedback({ analysingMessage: 'Loading SQL code' });
|
||||||
const sqlCodeRows = await this.analyserQuery('loadSqlCode', [
|
const sqlCodeRows = await this.analyserQuery('loadSqlCode', ['views', 'procedures', 'functions', 'triggers']);
|
||||||
'views',
|
|
||||||
'procedures',
|
|
||||||
'functions',
|
|
||||||
'triggers',
|
|
||||||
]);
|
|
||||||
const getCreateSql = row =>
|
const getCreateSql = row =>
|
||||||
sqlCodeRows.rows
|
sqlCodeRows.rows
|
||||||
.filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName)
|
.filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName)
|
||||||
@ -182,7 +179,6 @@ class MsSqlAnalyser extends DatabaseAnalyser {
|
|||||||
views,
|
views,
|
||||||
procedures,
|
procedures,
|
||||||
functions,
|
functions,
|
||||||
schemas,
|
|
||||||
defaultSchema: defaultSchemaRows.rows[0] ? defaultSchemaRows.rows[0].name : undefined,
|
defaultSchema: defaultSchemaRows.rows[0] ? defaultSchemaRows.rows[0].name : undefined,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -312,6 +312,17 @@ class Analyser extends DatabaseAnalyser {
|
|||||||
return res;
|
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() {
|
async _getFastSnapshot() {
|
||||||
const tableModificationsQueryData = this.driver.dialect.stringAgg
|
const tableModificationsQueryData = this.driver.dialect.stringAgg
|
||||||
? await this.analyserQuery('tableModifications')
|
? await this.analyserQuery('tableModifications')
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
module.exports = `select oid as "object_id", nspname as "schema_name" from pg_catalog.pg_namespace`;
|
@ -12,6 +12,7 @@ const matviewColumns = require('./matviewColumns');
|
|||||||
const indexes = require('./indexes');
|
const indexes = require('./indexes');
|
||||||
const indexcols = require('./indexcols');
|
const indexcols = require('./indexcols');
|
||||||
const uniqueNames = require('./uniqueNames');
|
const uniqueNames = require('./uniqueNames');
|
||||||
|
const getSchemas = require('./getSchemas');
|
||||||
const geometryColumns = require('./geometryColumns');
|
const geometryColumns = require('./geometryColumns');
|
||||||
const geographyColumns = require('./geographyColumns');
|
const geographyColumns = require('./geographyColumns');
|
||||||
|
|
||||||
@ -39,4 +40,5 @@ module.exports = {
|
|||||||
uniqueNames,
|
uniqueNames,
|
||||||
geometryColumns,
|
geometryColumns,
|
||||||
geographyColumns,
|
geographyColumns,
|
||||||
|
getSchemas,
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user