mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-04-25 02:56:00 +00:00
mssql - incremental analysis
This commit is contained in:
@@ -1,10 +1,10 @@
|
||||
const fp = require('lodash/fp');
|
||||
const _ = require('lodash');
|
||||
const sql = require('./sql')
|
||||
const sql = require('./sql');
|
||||
|
||||
const DatabaseAnalayser = require('../default/DatabaseAnalyser');
|
||||
|
||||
const byTableFilter = table => x => x.pureName == table.pureName && x.schemaName == x.schemaName;
|
||||
const byTableFilter = (table) => (x) => x.pureName == table.pureName && x.schemaName == x.schemaName;
|
||||
|
||||
function extractPrimaryKeys(table, pkColumns) {
|
||||
const filtered = pkColumns.filter(byTableFilter(table));
|
||||
@@ -18,7 +18,7 @@ function extractPrimaryKeys(table, pkColumns) {
|
||||
|
||||
function extractForeignKeys(table, fkColumns) {
|
||||
const grouped = _.groupBy(fkColumns.filter(byTableFilter(table)), 'constraintName');
|
||||
return _.keys(grouped).map(constraintName => ({
|
||||
return _.keys(grouped).map((constraintName) => ({
|
||||
constraintName,
|
||||
constraintType: 'foreignKey',
|
||||
..._.pick(grouped[constraintName][0], [
|
||||
@@ -34,6 +34,25 @@ function extractForeignKeys(table, fkColumns) {
|
||||
}));
|
||||
}
|
||||
|
||||
function objectTypeToField(type) {
|
||||
switch (type.trim()) {
|
||||
case 'U':
|
||||
return 'tables';
|
||||
case 'V':
|
||||
return 'views';
|
||||
case 'P':
|
||||
return 'procedures';
|
||||
case 'IF':
|
||||
case 'FN':
|
||||
case 'TF':
|
||||
return 'functions';
|
||||
case 'TR':
|
||||
return 'triggers';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {import('@dbgate/types').DbType} */
|
||||
function detectType(col) {
|
||||
switch (col.dataType) {
|
||||
@@ -172,37 +191,102 @@ class MsSqlAnalyser extends DatabaseAnalayser {
|
||||
super(pool, driver);
|
||||
}
|
||||
|
||||
async createQuery(
|
||||
resFileName,
|
||||
tables = false,
|
||||
views = false,
|
||||
procedures = false,
|
||||
functions = false,
|
||||
triggers = false
|
||||
) {
|
||||
createQuery(resFileName, filterIdObjects) {
|
||||
let res = sql[resFileName];
|
||||
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
|
||||
if (!this.modifications || !filterIdObjects || this.modifications.length == 0) {
|
||||
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
|
||||
} else {
|
||||
const filterIds = this.modifications
|
||||
.filter((x) => filterIdObjects.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
|
||||
.map((x) => x.objectId);
|
||||
if (filterIds.length == 0) {
|
||||
res = res.replace('=[OBJECT_ID_CONDITION]', ' = 0');
|
||||
} else {
|
||||
res = res.replace('=[OBJECT_ID_CONDITION]', ` in (${filterIds.join(',')})`);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
async runAnalysis() {
|
||||
const tables = await this.driver.query(this.pool, await this.createQuery('tables'));
|
||||
const columns = await this.driver.query(this.pool, await this.createQuery('columns'));
|
||||
const pkColumns = await this.driver.query(this.pool, await this.createQuery('primaryKeys'));
|
||||
const fkColumns = await this.driver.query(this.pool, await this.createQuery('foreignKeys'));
|
||||
async _runAnalysis() {
|
||||
const tables = await this.driver.query(this.pool, this.createQuery('tables', ['tables']));
|
||||
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables']));
|
||||
const pkColumns = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
|
||||
const fkColumns = await this.driver.query(this.pool, this.createQuery('foreignKeys', ['tables']));
|
||||
|
||||
this.result.tables = tables.rows.map(table => ({
|
||||
...table,
|
||||
columns: columns.rows
|
||||
.filter(col => col.objectId == table.objectId)
|
||||
.map(({ isNullable, isIdentity, ...col }) => ({
|
||||
...col,
|
||||
notNull: !isNullable,
|
||||
autoIncrement: !!isIdentity,
|
||||
commonType: detectType(col),
|
||||
})),
|
||||
primaryKey: extractPrimaryKeys(table, pkColumns.rows),
|
||||
foreignKeys: extractForeignKeys(table, fkColumns.rows),
|
||||
}));
|
||||
return this.mergeAnalyseResult({
|
||||
tables: tables.rows.map((table) => ({
|
||||
...table,
|
||||
columns: columns.rows
|
||||
.filter((col) => col.objectId == table.objectId)
|
||||
.map(({ isNullable, isIdentity, ...col }) => ({
|
||||
...col,
|
||||
notNull: !isNullable,
|
||||
autoIncrement: !!isIdentity,
|
||||
commonType: detectType(col),
|
||||
})),
|
||||
primaryKey: extractPrimaryKeys(table, pkColumns.rows),
|
||||
foreignKeys: extractForeignKeys(table, fkColumns.rows),
|
||||
})),
|
||||
});
|
||||
}
|
||||
|
||||
getDeletedObjectsForField(idArray, objectTypeField) {
|
||||
return this.structure[objectTypeField]
|
||||
.filter((x) => !idArray.includes(x.objectId))
|
||||
.map((x) => ({
|
||||
oldName: _.pick(x, ['schemaName', 'pureName']),
|
||||
objectId: x.objectId,
|
||||
action: 'remove',
|
||||
objectTypeField,
|
||||
}));
|
||||
}
|
||||
|
||||
getDeletedObjects(idArray) {
|
||||
return [
|
||||
...this.getDeletedObjectsForField(idArray, 'tables'),
|
||||
...this.getDeletedObjectsForField(idArray, 'views'),
|
||||
...this.getDeletedObjectsForField(idArray, 'procedures'),
|
||||
...this.getDeletedObjectsForField(idArray, 'functions'),
|
||||
...this.getDeletedObjectsForField(idArray, 'triggers'),
|
||||
];
|
||||
}
|
||||
|
||||
async getModifications() {
|
||||
const modificationsQueryData = await this.driver.query(this.pool, this.createQuery('modifications'));
|
||||
// console.log('MOD - SRC', modifications);
|
||||
// console.log(
|
||||
// 'MODs',
|
||||
// this.structure.tables.map((x) => x.modifyDate)
|
||||
// );
|
||||
const modifications = modificationsQueryData.rows.map((x) => {
|
||||
const { type, objectId, modifyDate, schemaName, pureName } = x;
|
||||
const field = objectTypeToField(type);
|
||||
if (!this.structure[field]) return null;
|
||||
// @ts-ignore
|
||||
const obj = this.structure[field].find((x) => x.objectId == objectId);
|
||||
|
||||
// object not modified
|
||||
if (obj && Math.abs(new Date(modifyDate).getTime() - new Date(obj.modifyDate).getTime()) < 1000) return null;
|
||||
|
||||
/** @type {import('@dbgate/types').DatabaseModification} */
|
||||
const action = obj
|
||||
? {
|
||||
newName: { schemaName, pureName },
|
||||
oldName: _.pick(obj, ['schemaName', 'pureName']),
|
||||
action: 'change',
|
||||
objectTypeField: field,
|
||||
objectId,
|
||||
}
|
||||
: {
|
||||
newName: { schemaName, pureName },
|
||||
action: 'add',
|
||||
objectTypeField: field,
|
||||
objectId,
|
||||
};
|
||||
return action;
|
||||
});
|
||||
|
||||
return [..._.compact(modifications), ...this.getDeletedObjects(modificationsQueryData.rows.map((x) => x.objectId))];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -109,10 +109,12 @@ const driver = {
|
||||
},
|
||||
async analyseFull(pool) {
|
||||
const analyser = new MsSqlAnalyser(pool, this);
|
||||
await analyser.runAnalysis();
|
||||
return analyser.result;
|
||||
return await analyser.fullAnalysis();
|
||||
},
|
||||
async analyseIncremental(pool, structure) {
|
||||
const analyser = new MsSqlAnalyser(pool, this);
|
||||
return await analyser.incrementalAnalysis(structure);
|
||||
},
|
||||
// async analyseIncremental(pool) {},
|
||||
createDumper() {
|
||||
return new MsSqlDumper(this);
|
||||
},
|
||||
|
||||
@@ -2,10 +2,12 @@ const columns = require('./columns');
|
||||
const foreignKeys = require('./foreignKeys');
|
||||
const primaryKeys = require('./primaryKeys');
|
||||
const tables = require('./tables');
|
||||
const modifications = require('./modifications');
|
||||
|
||||
module.exports = {
|
||||
columns,
|
||||
tables,
|
||||
foreignKeys,
|
||||
primaryKeys,
|
||||
modifications,
|
||||
};
|
||||
|
||||
6
packages/engines/mssql/sql/modifications.js
Normal file
6
packages/engines/mssql/sql/modifications.js
Normal file
@@ -0,0 +1,6 @@
|
||||
module.exports = `
|
||||
select o.object_id as objectId, o.modify_date as modifyDate, o.type, o.name as pureName, s.name as schemaName
|
||||
from sys.objects o
|
||||
inner join sys.schemas s on o.schema_id = s.schema_id
|
||||
where o.type in ('U', 'V', 'P', 'IF', 'FN', 'TR', 'TF')
|
||||
`;
|
||||
@@ -1,7 +1,7 @@
|
||||
module.exports = `
|
||||
select
|
||||
o.name as pureName, s.name as schemaName, o.object_id as objectId,
|
||||
o.create_date, o.modify_date
|
||||
o.create_date as createDate, o.modify_date as modifyDate
|
||||
from sys.tables o
|
||||
inner join sys.schemas s on o.schema_id = s.schema_id
|
||||
where o.object_id =[OBJECT_ID_CONDITION]
|
||||
|
||||
Reference in New Issue
Block a user