DatabaseAnalyser.createQuery core moved to base class

This commit is contained in:
Jan Prochazka 2021-05-15 18:41:30 +02:00
parent 4a4c4b41c0
commit 2eb1c04fcf
18 changed files with 71 additions and 105 deletions

View File

@ -8,6 +8,7 @@ export class DatabaseAnalyser {
structure: DatabaseInfo;
modifications: DatabaseModification[];
singleObjectFilter: any;
singleObjectId: string = null;
constructor(public pool, public driver: EngineDriver) {}
@ -15,6 +16,8 @@ export class DatabaseAnalyser {
return DatabaseAnalyser.createEmptyStructure();
}
async _computeSingleObjectId() {}
/** @returns {Promise<import('dbgate-types').DatabaseModification[]>} */
async getModifications() {
if (this.structure == null) throw new Error('DatabaseAnalyse.getModifications - structure must be filled');
@ -28,6 +31,7 @@ export class DatabaseAnalyser {
async singleObjectAnalysis(name, typeField) {
this.singleObjectFilter = { ...name, typeField };
await this._computeSingleObjectId();
const res = this._runAnalysis();
if (res[typeField].length == 1) return res[typeField][0];
const obj = res[typeField].find(x => x.pureName == name.pureName && x.schemaName == name.schemaName);
@ -94,6 +98,29 @@ export class DatabaseAnalyser {
// return this.structure.tables.find((x) => x.objectId == id);
// }
createQuery(template, typeFields) {
let res = template;
if (this.singleObjectFilter) {
const { typeField } = this.singleObjectFilter;
if (!this.singleObjectId) return null;
if (!typeFields || !typeFields.includes(typeField)) return null;
return res.replace(/=OBJECT_ID_CONDITION/g, ` = ${this.singleObjectId}`);
}
if (!this.modifications || !typeFields || this.modifications.length == 0) {
res = res.replace(/=OBJECT_ID_CONDITION/g, ' is not null');
} else {
const filterIds = this.modifications
.filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
.map(x => x.objectId);
if (filterIds.length == 0) {
res = res.replace(/=OBJECT_ID_CONDITION/g, ' = 0');
} else {
res = res.replace(/=OBJECT_ID_CONDITION/g, ` in (${filterIds.join(',')})`);
}
}
return res;
}
static createEmptyStructure(): DatabaseInfo {
return {
tables: [],

View File

@ -48,43 +48,20 @@ function getColumnInfo({
class MsSqlAnalyser extends DatabaseAnalyser {
constructor(pool, driver) {
super(pool, driver);
this.singleObjectId = null;
}
createQuery(resFileName, typeFields) {
let res = sql[resFileName];
if (this.singleObjectFilter) {
const { typeField } = this.singleObjectFilter;
if (!this.singleObjectId) return null;
if (!typeFields || !typeFields.includes(typeField)) return null;
return res.replace('=[OBJECT_ID_CONDITION]', ` = ${this.singleObjectId}`);
}
if (!this.modifications || !typeFields || this.modifications.length == 0) {
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
} else {
const filterIds = this.modifications
.filter((x) => typeFields.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;
return super.createQuery(sql[resFileName], typeFields);
}
async getSingleObjectId() {
if (this.singleObjectFilter) {
const { schemaName, pureName, typeField } = this.singleObjectFilter;
const fullName = schemaName ? `[${schemaName}].[${pureName}]` : pureName;
const resId = await this.driver.query(this.pool, `SELECT OBJECT_ID('${fullName}') AS id`);
this.singleObjectId = resId.rows[0].id;
}
async _computeSingleObjectId() {
const { schemaName, pureName, typeField } = this.singleObjectFilter;
const fullName = schemaName ? `[${schemaName}].[${pureName}]` : pureName;
const resId = await this.driver.query(this.pool, `SELECT OBJECT_ID('${fullName}') AS id`);
this.singleObjectId = resId.rows[0].id;
}
async _runAnalysis() {
await this.getSingleObjectId();
const tablesRows = await this.driver.query(this.pool, this.createQuery('tables', ['tables']));
const columnsRows = await this.driver.query(this.pool, this.createQuery('columns', ['tables']));
const pkColumnsRows = await this.driver.query(this.pool, this.createQuery('primaryKeys', ['tables']));
@ -97,10 +74,10 @@ class MsSqlAnalyser extends DatabaseAnalyser {
this.pool,
this.createQuery('loadSqlCode', ['views', 'procedures', 'functions', 'triggers'])
);
const getCreateSql = (row) =>
const getCreateSql = row =>
sqlCodeRows.rows
.filter((x) => x.pureName == row.pureName && x.schemaName == row.schemaName)
.map((x) => x.codeText)
.filter(x => x.pureName == row.pureName && x.schemaName == row.schemaName)
.map(x => x.codeText)
.join('');
const viewsRows = await this.driver.query(this.pool, this.createQuery('views', ['views']));
const programmableRows = await this.driver.query(
@ -109,29 +86,29 @@ class MsSqlAnalyser extends DatabaseAnalyser {
);
const viewColumnRows = await this.driver.query(this.pool, this.createQuery('viewColumns', ['views']));
const tables = tablesRows.rows.map((row) => ({
const tables = tablesRows.rows.map(row => ({
...row,
columns: columnsRows.rows.filter((col) => col.objectId == row.objectId).map(getColumnInfo),
columns: columnsRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(row, pkColumnsRows.rows),
foreignKeys: DatabaseAnalyser.extractForeignKeys(row, fkColumnsRows.rows),
}));
const views = viewsRows.rows.map((row) => ({
const views = viewsRows.rows.map(row => ({
...row,
createSql: getCreateSql(row),
columns: viewColumnRows.rows.filter((col) => col.objectId == row.objectId).map(getColumnInfo),
columns: viewColumnRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo),
}));
const procedures = programmableRows.rows
.filter((x) => x.sqlObjectType.trim() == 'P')
.map((row) => ({
.filter(x => x.sqlObjectType.trim() == 'P')
.map(row => ({
...row,
createSql: getCreateSql(row),
}));
const functions = programmableRows.rows
.filter((x) => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim()))
.map((row) => ({
.filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim()))
.map(row => ({
...row,
createSql: getCreateSql(row),
}));
@ -147,8 +124,8 @@ class MsSqlAnalyser extends DatabaseAnalyser {
getDeletedObjectsForField(idArray, objectTypeField) {
return this.structure[objectTypeField]
.filter((x) => !idArray.includes(x.objectId))
.map((x) => ({
.filter(x => !idArray.includes(x.objectId))
.map(x => ({
oldName: _.pick(x, ['schemaName', 'pureName']),
objectId: x.objectId,
action: 'remove',
@ -173,12 +150,12 @@ class MsSqlAnalyser extends DatabaseAnalyser {
// 'MODs',
// this.structure.tables.map((x) => x.modifyDate)
// );
const modifications = modificationsQueryData.rows.map((x) => {
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);
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;
@ -201,7 +178,7 @@ class MsSqlAnalyser extends DatabaseAnalyser {
return action;
});
return [..._.compact(modifications), ...this.getDeletedObjects(modificationsQueryData.rows.map((x) => x.objectId))];
return [..._.compact(modifications), ...this.getDeletedObjects(modificationsQueryData.rows.map(x => x.objectId))];
}
}

View File

@ -15,6 +15,6 @@ INNER JOIN sys.schemas u ON u.schema_id=o.schema_id
INNER JOIN INFORMATION_SCHEMA.COLUMNS col ON col.TABLE_NAME = o.name AND col.TABLE_SCHEMA = u.name and col.COLUMN_NAME = c.name
left join sys.default_constraints d on c.default_object_id = d.object_id
left join sys.computed_columns m on m.object_id = c.object_id and m.column_id = c.column_id
where o.type = 'U' and o.object_id =[OBJECT_ID_CONDITION]
where o.type = 'U' and o.object_id =OBJECT_ID_CONDITION
order by c.column_id
`;

View File

@ -36,5 +36,5 @@ LEFT JOIN sys.schemas IXS ON IXT.schema_id = IXS.schema_id
inner join sys.objects o on FK.TABLE_NAME = o.name
inner join sys.schemas s on o.schema_id = s.schema_id and FK.TABLE_SCHEMA = s.name
where o.object_id =[OBJECT_ID_CONDITION]
where o.object_id =OBJECT_ID_CONDITION
`;

View File

@ -3,6 +3,6 @@ select s.name as pureName, u.name as schemaName, c.text AS codeText
from sys.objects s
inner join sys.syscomments c on s.object_id = c.id
inner join sys.schemas u on u.schema_id = s.schema_id
where (s.object_id =[OBJECT_ID_CONDITION])
where (s.object_id =OBJECT_ID_CONDITION)
order by u.name, s.name, c.colid
`;

View File

@ -10,5 +10,5 @@ where
and o.schema_id = s.schema_id and t.Table_Schema = s.name
and c.Table_Name = t.Table_Name
and Constraint_Type = 'PRIMARY KEY'
and o.object_id =[OBJECT_ID_CONDITION]
and o.object_id =OBJECT_ID_CONDITION
`;

View File

@ -2,5 +2,5 @@ module.exports = `
select o.name as pureName, s.name as schemaName, o.object_id as objectId, o.create_date as createDate, o.modify_date as modifyDate, o.type as sqlObjectType
from sys.objects o
inner join sys.schemas s on o.schema_id = s.schema_id
where o.type in ('P', 'IF', 'FN', 'TF') and o.object_id =[OBJECT_ID_CONDITION]
where o.type in ('P', 'IF', 'FN', 'TF') and o.object_id =OBJECT_ID_CONDITION
`;

View File

@ -4,5 +4,5 @@ select
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]
where o.object_id =OBJECT_ID_CONDITION
`;

View File

@ -13,6 +13,6 @@ select
FROM sys.objects o
INNER JOIN sys.schemas u ON u.schema_id=o.schema_id
INNER JOIN INFORMATION_SCHEMA.COLUMNS col ON col.TABLE_NAME = o.name AND col.TABLE_SCHEMA = u.name
WHERE o.type in ('V') and o.object_id =[OBJECT_ID_CONDITION]
WHERE o.type in ('V') and o.object_id =OBJECT_ID_CONDITION
order by col.ORDINAL_POSITION
`;

View File

@ -6,5 +6,5 @@ SELECT
o.create_date as createDate,
o.modify_date as modifyDate
FROM sys.objects o INNER JOIN sys.schemas u ON u.schema_id=o.schema_id
WHERE type in ('V') and o.object_id =[OBJECT_ID_CONDITION]
WHERE type in ('V') and o.object_id =OBJECT_ID_CONDITION
`;

View File

@ -42,27 +42,8 @@ class Analyser extends DatabaseAnalyser {
createQuery(resFileName, typeFields) {
let res = sql[resFileName];
if (this.singleObjectFilter) {
const { typeField, pureName } = this.singleObjectFilter;
if (!typeFields || !typeFields.includes(typeField)) return null;
res = res.replace('=[OBJECT_ID_CONDITION]', ` = '${pureName}'`).replace('#DATABASE#', this.pool._database_name);
return res;
}
if (!this.modifications || !typeFields || this.modifications.length == 0) {
res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
} else {
const filterNames = this.modifications
.filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
.map(x => x.newName && x.newName.pureName)
.filter(Boolean);
if (filterNames.length == 0) {
res = res.replace('=[OBJECT_ID_CONDITION]', ' IS NULL');
} else {
res = res.replace('=[OBJECT_ID_CONDITION]', ` in (${filterNames.map(x => `'${x}'`).join(',')})`);
}
}
res = res.replace('#DATABASE#', this.pool._database_name);
return res;
return super.createQuery(res, typeFields);
}
getRequestedViewNames(allViewNames) {

View File

@ -10,6 +10,6 @@ select
COLUMN_DEFAULT as defaultValue,
EXTRA as extra
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_ID_CONDITION]
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =OBJECT_ID_CONDITION
order by ORDINAL_POSITION
`;

View File

@ -12,6 +12,6 @@ inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE
on REFERENTIAL_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME
and REFERENTIAL_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME
and REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA
where REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and REFERENTIAL_CONSTRAINTS.TABLE_NAME =[OBJECT_ID_CONDITION]
where REFERENTIAL_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and REFERENTIAL_CONSTRAINTS.TABLE_NAME =OBJECT_ID_CONDITION
order by KEY_COLUMN_USAGE.ORDINAL_POSITION
`;

View File

@ -7,6 +7,6 @@ inner join INFORMATION_SCHEMA.KEY_COLUMN_USAGE
on TABLE_CONSTRAINTS.TABLE_NAME = KEY_COLUMN_USAGE.TABLE_NAME
and TABLE_CONSTRAINTS.CONSTRAINT_NAME = KEY_COLUMN_USAGE.CONSTRAINT_NAME
and TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA
where TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and TABLE_CONSTRAINTS.TABLE_NAME =[OBJECT_ID_CONDITION] AND TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY'
where TABLE_CONSTRAINTS.CONSTRAINT_SCHEMA = '#DATABASE#' and TABLE_CONSTRAINTS.TABLE_NAME =OBJECT_ID_CONDITION AND TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY'
order by KEY_COLUMN_USAGE.ORDINAL_POSITION
`;

View File

@ -5,5 +5,5 @@ select
COALESCE(LAST_ALTERED, CREATED) as modifyDate,
ROUTINE_DEFINITION as createSql
from information_schema.routines
where ROUTINE_SCHEMA = '#DATABASE#' and ROUTINE_NAME =[OBJECT_ID_CONDITION]
where ROUTINE_SCHEMA = '#DATABASE#' and ROUTINE_NAME =OBJECT_ID_CONDITION
`;

View File

@ -3,5 +3,5 @@ select
TABLE_NAME as pureName,
case when ENGINE='InnoDB' then CREATE_TIME else coalesce(UPDATE_TIME, CREATE_TIME) end as modifyDate
from information_schema.tables
where TABLE_SCHEMA = '#DATABASE#' and TABLE_TYPE='BASE TABLE' and TABLE_NAME =[OBJECT_ID_CONDITION];
where TABLE_SCHEMA = '#DATABASE#' and TABLE_TYPE='BASE TABLE' and TABLE_NAME =OBJECT_ID_CONDITION;
`;

View File

@ -3,5 +3,5 @@ select
TABLE_NAME as pureName,
coalesce(UPDATE_TIME, CREATE_TIME) as modifyDate
from information_schema.tables
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =[OBJECT_ID_CONDITION] and TABLE_TYPE = 'VIEW';
where TABLE_SCHEMA = '#DATABASE#' and TABLE_NAME =OBJECT_ID_CONDITION and TABLE_TYPE = 'VIEW';
`;

View File

@ -41,33 +41,14 @@ class Analyser extends DatabaseAnalyser {
}
createQuery(resFileName, typeFields) {
let res = sql[resFileName];
if (this.singleObjectFilter) {
const { typeField, schemaName, pureName } = this.singleObjectFilter;
if (!typeFields || !typeFields.includes(typeField)) return null;
res = res.replace(/=OBJECT_ID_CONDITION/g, ` = '${typeField}:${schemaName || 'public'}.${pureName}'`);
return res;
}
if (!this.modifications || !typeFields || this.modifications.length == 0) {
res = res.replace(/=OBJECT_ID_CONDITION/g, ' is not null');
} else {
const filterNames = this.modifications
.filter(x => typeFields.includes(x.objectTypeField) && (x.action == 'add' || x.action == 'change'))
.filter(x => x.newName)
.map(x => `${x.objectTypeField}:${x.newName.schemaName}.${x.newName.pureName}`);
if (filterNames.length == 0) {
res = res.replace(/=OBJECT_ID_CONDITION/g, ' IS NULL');
} else {
res = res.replace(/=OBJECT_ID_CONDITION/g, ` in (${filterNames.map(x => `'${x}'`).join(',')})`);
}
}
return res;
// let res = sql[resFileName];
// res = res.replace('=[OBJECT_ID_CONDITION]', ' is not null');
// return res;
return super.createQuery(sql[resFileName], typeFields);
}
async _computeSingleObjectId() {
const { typeField, schemaName, pureName } = this.singleObjectFilter;
this.singleObjectId = `${typeField}:${schemaName || 'public'}.${pureName}`;
}
async _runAnalysis() {
const tables = await this.driver.query(this.pool, this.createQuery('tableModifications', ['tables']));
const columns = await this.driver.query(this.pool, this.createQuery('columns', ['tables']));