diff --git a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js index 94b5c6382..682cdeb48 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js +++ b/plugins/dbgate-plugin-mssql/src/backend/MsSqlAnalyser.js @@ -1,4 +1,5 @@ const _ = require('lodash'); +const crypto = require('crypto'); const sql = require('./sql'); const { DatabaseAnalyser, isTypeString, isTypeNumeric } = global.DBGATE_PACKAGES['dbgate-tools']; @@ -55,6 +56,7 @@ function getColumnInfo({ defaultConstraint, computedExpression, columnComment, + objectId, }) { const fullDataType = getFullDataTypeName({ dataType, @@ -72,6 +74,7 @@ function getColumnInfo({ } return { + objectId, columnName, dataType: fullDataType, notNull: !isNullable, @@ -84,6 +87,31 @@ function getColumnInfo({ }; } +/** + * @param {ReturnType} fieldType + * @param {any} item + * @param {Array>} columns + * @returns {string|null} + */ +function createObjectContentHash(fieldType, item, columns) { + if (!fieldType) return null; + const { modifyDate } = item; + + if ((columns?.length && fieldType === 'tables') || fieldType === 'views') { + const modifyDateStr = modifyDate ? modifyDate.toISOString() : ''; + const objectColumns = columns.filter(col => col.objectId == item.objectId); + const colsComments = objectColumns.map(i => i.columnComment).join(','); + + return crypto + .createHash('sha256') + .update(modifyDateStr + colsComments) + .digest('hex'); + } + + if (!modifyDate) return null; + return modifyDate.toISOString(); +} + class MsSqlAnalyser extends DatabaseAnalyser { constructor(dbhan, driver, version) { super(dbhan, driver, version); @@ -106,6 +134,7 @@ class MsSqlAnalyser extends DatabaseAnalyser { const tablesRows = await this.analyserQuery('tables', ['tables']); this.feedback({ analysingMessage: 'Loading columns' }); const columnsRows = await this.analyserQuery('columns', ['tables']); + const columns = columnsRows.rows.map(getColumnInfo); this.feedback({ analysingMessage: 'Loading primary keys' }); const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']); @@ -145,8 +174,8 @@ class MsSqlAnalyser extends DatabaseAnalyser { this.feedback({ analysingMessage: 'Finalizing DB structure' }); const tables = tablesRows.rows.map(row => ({ ...row, - contentHash: row.modifyDate && row.modifyDate.toISOString(), - columns: columnsRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo), + contentHash: createObjectContentHash('tables', row, columns), + columns: columns.filter(col => col.objectId == row.objectId), primaryKey: DatabaseAnalyser.extractPrimaryKeys(row, pkColumnsRows.rows), foreignKeys: DatabaseAnalyser.extractForeignKeys(row, fkColumnsRows.rows), indexes: indexesRows.rows @@ -174,7 +203,7 @@ class MsSqlAnalyser extends DatabaseAnalyser { const views = viewsRows.rows.map(row => ({ ...row, - contentHash: row.modifyDate && row.modifyDate.toISOString(), + contentHash: createObjectContentHash('views', row, columns), createSql: getCreateSql(row), columns: viewColumnRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo), })); @@ -195,7 +224,7 @@ class MsSqlAnalyser extends DatabaseAnalyser { .filter(x => x.sqlObjectType.trim() == 'P') .map(row => ({ ...row, - contentHash: row.modifyDate && row.modifyDate.toISOString(), + contentHash: createObjectContentHash('procedures', row), createSql: getCreateSql(row), parameters: prodceureToParameters[row.objectId], })); @@ -216,14 +245,14 @@ class MsSqlAnalyser extends DatabaseAnalyser { .filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim())) .map(row => ({ ...row, - contentHash: row.modifyDate && row.modifyDate.toISOString(), + contentHash: createObjectContentHash('functions', row), createSql: getCreateSql(row), parameters: functionToParameters[row.objectId], })); const triggers = triggerRows.rows.map(row => ({ objectId: `triggers:${row.objectId}`, - contentHash: row.modifyDate && row.modifyDate.toISOString(), + contentHash: createObjectContentHash('triggers', row), createSql: row.definition, triggerTiming: row.triggerTiming, eventType: row.eventType, @@ -244,17 +273,19 @@ class MsSqlAnalyser extends DatabaseAnalyser { async _getFastSnapshot() { const modificationsQueryData = await this.analyserQuery('modifications'); + const columnsRows = await this.analyserQuery('columns', ['tables']); + const columns = columnsRows.rows.map(getColumnInfo); const tableSizes = await this.analyserQuery('tableSizes'); const res = DatabaseAnalyser.createEmptyStructure(); for (const item of modificationsQueryData.rows) { - const { type, objectId, modifyDate, schemaName, pureName } = item; + const { type, objectId, schemaName, pureName } = item; const field = objectTypeToField(type); if (!field || !res[field]) continue; res[field].push({ objectId, - contentHash: modifyDate && modifyDate.toISOString(), + contentHash: createObjectContentHash(field, item, columns), schemaName, pureName, });