Merge pull request #1171 from dbgate/feature/1137-mssql-column-desc

feat: add MS_Description to mssql analyzer columns
This commit is contained in:
Jan Prochazka
2025-08-13 06:55:35 +02:00
committed by GitHub
14 changed files with 350 additions and 39 deletions

View File

@@ -118,6 +118,31 @@ describe('Alter table', () => {
}) })
); );
test.each(engines.filter(i => i.supportTableComments).map(engine => [engine.label, engine]))(
'Add comment to table - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(engine, conn, driver, tbl => {
tbl.objectComment = 'Added table comment';
});
})
);
test.each(engines.filter(i => i.supportColumnComments).map(engine => [engine.label, engine]))(
'Add comment to column - %s',
testWrapper(async (conn, driver, engine) => {
await testTableDiff(engine, conn, driver, tbl => {
tbl.columns.push({
columnName: 'added',
columnComment: 'Added column comment',
dataType: 'int',
pairingId: crypto.randomUUID(),
notNull: false,
autoIncrement: false,
});
});
})
);
test.each( test.each(
createEnginesColumnsSource(engines.filter(x => !x.skipDropColumn)).filter( createEnginesColumnsSource(engines.filter(x => !x.skipDropColumn)).filter(
([_label, col, engine]) => !engine.skipPkDrop || !col.endsWith('_pk') ([_label, col, engine]) => !engine.skipPkDrop || !col.endsWith('_pk')

View File

@@ -64,6 +64,40 @@ describe('Table create', () => {
}) })
); );
test.each(
engines.filter(i => i.supportTableComments || i.supportColumnComments).map(engine => [engine.label, engine])
)(
'Simple table with comment - %s',
testWrapper(async (conn, driver, engine) => {
await testTableCreate(engine, conn, driver, {
...(engine.supportTableComments && {
schemaName: 'dbo',
objectComment: 'table comment',
}),
...(engine.defaultSchemaName && {
schemaName: engine.defaultSchemaName,
}),
columns: [
{
columnName: 'col1',
dataType: 'int',
pureName: 'tested',
...(engine.skipNullability ? {} : { notNull: true }),
...(engine.supportColumnComments && {
columnComment: 'column comment',
}),
...(engine.defaultSchemaName && {
schemaName: engine.defaultSchemaName,
}),
},
],
primaryKey: {
columns: [{ columnName: 'col1' }],
},
});
})
);
test.each(engines.filter(x => !x.skipIndexes).map(engine => [engine.label, engine]))( test.each(engines.filter(x => !x.skipIndexes).map(engine => [engine.label, engine]))(
'Table with index - %s', 'Table with index - %s',
testWrapper(async (conn, driver, engine) => { testWrapper(async (conn, driver, engine) => {

View File

@@ -443,6 +443,8 @@ const sqlServerEngine = {
supportSchemas: true, supportSchemas: true,
supportRenameSqlObject: true, supportRenameSqlObject: true,
defaultSchemaName: 'dbo', defaultSchemaName: 'dbo',
supportTableComments: true,
supportColumnComments: true,
// skipSeparateSchemas: true, // skipSeparateSchemas: true,
triggers: [ triggers: [
{ {

View File

@@ -292,6 +292,16 @@ export class AlterPlan {
} }
} }
_hasOnlyCommentChange(op: AlterOperation): boolean {
if (op.operationType === 'changeColumn') {
return _.isEqual(
_.omit(op.oldObject, ['columnComment', 'ordinal']),
_.omit(op.newObject, ['columnComment', 'ordinal'])
);
}
return false;
}
_getDependendColumnConstraints(column: ColumnInfo, dependencyDefinition) { _getDependendColumnConstraints(column: ColumnInfo, dependencyDefinition) {
const table = this.wholeOldDb.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName); const table = this.wholeOldDb.tables.find(x => x.pureName == column.pureName && x.schemaName == column.schemaName);
if (!table) return []; if (!table) return [];
@@ -337,31 +347,42 @@ export class AlterPlan {
]) { ]) {
if (op.operationType == testedOperationType) { if (op.operationType == testedOperationType) {
const constraints = this._getDependendColumnConstraints(testedObject as ColumnInfo, testedDependencies); const constraints = this._getDependendColumnConstraints(testedObject as ColumnInfo, testedDependencies);
const ignoreContraints = this.dialect.safeCommentChanges && this._hasOnlyCommentChange(op);
// if (constraints.length > 0 && this.opts.noDropConstraint) { // if (constraints.length > 0 && this.opts.noDropConstraint) {
// return []; // return [];
// } // }
const res: AlterOperation[] = [ const res: AlterOperation[] = [];
...constraints.map(oldObject => {
const opRes: AlterOperation = {
operationType: 'dropConstraint',
oldObject,
isRecreate: true,
};
return opRes;
}),
op,
..._.reverse([...constraints]).map(newObject => {
const opRes: AlterOperation = {
operationType: 'createConstraint',
newObject,
};
return opRes;
}),
];
if (constraints.length > 0) { if (!ignoreContraints) {
res.push(
...constraints.map(oldObject => {
const opRes: AlterOperation = {
operationType: 'dropConstraint',
oldObject,
isRecreate: true,
};
return opRes;
})
);
}
res.push(op);
if (!ignoreContraints) {
res.push(
..._.reverse([...constraints]).map(newObject => {
const opRes: AlterOperation = {
operationType: 'createConstraint',
newObject,
};
return opRes;
})
);
}
if (!ignoreContraints && constraints.length > 0) {
this.recreates.constraints += 1; this.recreates.constraints += 1;
} }
return res; return res;

View File

@@ -59,6 +59,7 @@ export interface DbDiffOptions {
ignoreForeignKeyActions?: boolean; ignoreForeignKeyActions?: boolean;
ignoreDataTypes?: boolean; ignoreDataTypes?: boolean;
ignoreComments?: boolean;
} }
export function generateTablePairingId(table: TableInfo): TableInfo { export function generateTablePairingId(table: TableInfo): TableInfo {
@@ -322,11 +323,14 @@ export function testEqualColumns(
); );
return false; return false;
} }
if ((a.columnComment || '') != (b.columnComment || '')) {
console.debug( if (!opts.ignoreComments) {
`Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different comment: ${a.columnComment}, ${b.columnComment}` if ((a.columnComment || '') != (b.columnComment || '')) {
); console.debug(
return false; `Column ${a.pureName}.${a.columnName}, ${b.pureName}.${b.columnName}: different comment: ${a.columnComment}, ${b.columnComment}`
);
return false;
}
} }
if (!testEqualTypes(a, b, opts)) { if (!testEqualTypes(a, b, opts)) {

View File

@@ -74,6 +74,14 @@ export interface SqlDialect {
predefinedDataTypes: string[]; predefinedDataTypes: string[];
columnProperties?: {
columnName?: boolean;
isSparse?: true;
isPersisted?: true;
};
safeCommentChanges?: boolean;
// create sql-tree expression // create sql-tree expression
createColumnViewExpression( createColumnViewExpression(
columnName: string, columnName: string,

View File

@@ -56,6 +56,9 @@ export type TestEngineInfo = {
useTextTypeForStrings?: boolean; useTextTypeForStrings?: boolean;
supportTableComments?: boolean;
supportColumnComments?: boolean;
supportRenameSqlObject?: boolean; supportRenameSqlObject?: boolean;
supportSchemas?: boolean; supportSchemas?: boolean;

View File

@@ -1,4 +1,5 @@
const _ = require('lodash'); const _ = require('lodash');
const crypto = require('crypto');
const sql = require('./sql'); const sql = require('./sql');
const { DatabaseAnalyser, isTypeString, isTypeNumeric } = global.DBGATE_PACKAGES['dbgate-tools']; const { DatabaseAnalyser, isTypeString, isTypeNumeric } = global.DBGATE_PACKAGES['dbgate-tools'];
@@ -54,6 +55,8 @@ function getColumnInfo({
defaultValue, defaultValue,
defaultConstraint, defaultConstraint,
computedExpression, computedExpression,
columnComment,
objectId,
}) { }) {
const fullDataType = getFullDataTypeName({ const fullDataType = getFullDataTypeName({
dataType, dataType,
@@ -71,6 +74,7 @@ function getColumnInfo({
} }
return { return {
objectId,
columnName, columnName,
dataType: fullDataType, dataType: fullDataType,
notNull: !isNullable, notNull: !isNullable,
@@ -79,9 +83,36 @@ function getColumnInfo({
defaultConstraint, defaultConstraint,
computedExpression: simplifyComutedExpression(computedExpression), computedExpression: simplifyComutedExpression(computedExpression),
hasAutoValue: !!(dataType == 'timestamp' || dataType == 'rowversion' || computedExpression), hasAutoValue: !!(dataType == 'timestamp' || dataType == 'rowversion' || computedExpression),
columnComment,
}; };
} }
/**
* @param {ReturnType<objectTypeToField>} fieldType
* @param {any} item
* @param {Array<{ objectId: string; columnId: number, columnComment: string }>} 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
.filter(i => i.columnComment)
.map(i => `${i.columnId}/${i.columnComment}`)
.join('||');
const objectComment = item.objectComment || '';
return crypto.createHash('sha256').update(`${modifyDateStr}:${colsComments}:${objectComment}`).digest('hex');
}
if (!modifyDate) return null;
return modifyDate.toISOString();
}
class MsSqlAnalyser extends DatabaseAnalyser { class MsSqlAnalyser extends DatabaseAnalyser {
constructor(dbhan, driver, version) { constructor(dbhan, driver, version) {
super(dbhan, driver, version); super(dbhan, driver, version);
@@ -104,6 +135,9 @@ class MsSqlAnalyser extends DatabaseAnalyser {
const tablesRows = await this.analyserQuery('tables', ['tables']); const tablesRows = await this.analyserQuery('tables', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00206 Loading columns' }); this.feedback({ analysingMessage: 'DBGM-00206 Loading columns' });
const columnsRows = await this.analyserQuery('columns', ['tables']); const columnsRows = await this.analyserQuery('columns', ['tables']);
const columns = columnsRows.rows.map(getColumnInfo);
const baseColumnsRows = await this.analyserQuery('baseColumns', ['tables']);
const baseColumns = baseColumnsRows.rows.map(getColumnInfo);
this.feedback({ analysingMessage: 'DBGM-00207 Loading primary keys' }); this.feedback({ analysingMessage: 'DBGM-00207 Loading primary keys' });
const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']); const pkColumnsRows = await this.analyserQuery('primaryKeys', ['tables']);
this.feedback({ analysingMessage: 'DBGM-00208 Loading foreign keys' }); this.feedback({ analysingMessage: 'DBGM-00208 Loading foreign keys' });
@@ -142,8 +176,8 @@ class MsSqlAnalyser extends DatabaseAnalyser {
this.feedback({ analysingMessage: 'DBGM-00217 Finalizing DB structure' }); this.feedback({ analysingMessage: 'DBGM-00217 Finalizing DB structure' });
const tables = tablesRows.rows.map(row => ({ const tables = tablesRows.rows.map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: createObjectContentHash('tables', row, baseColumns),
columns: columnsRows.rows.filter(col => col.objectId == row.objectId).map(getColumnInfo), columns: columns.filter(col => col.objectId == row.objectId),
primaryKey: DatabaseAnalyser.extractPrimaryKeys(row, pkColumnsRows.rows), primaryKey: DatabaseAnalyser.extractPrimaryKeys(row, pkColumnsRows.rows),
foreignKeys: DatabaseAnalyser.extractForeignKeys(row, fkColumnsRows.rows), foreignKeys: DatabaseAnalyser.extractForeignKeys(row, fkColumnsRows.rows),
indexes: indexesRows.rows indexes: indexesRows.rows
@@ -171,7 +205,7 @@ class MsSqlAnalyser extends DatabaseAnalyser {
const views = viewsRows.rows.map(row => ({ const views = viewsRows.rows.map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: createObjectContentHash('views', row, baseColumns),
createSql: getCreateSql(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),
})); }));
@@ -192,7 +226,7 @@ class MsSqlAnalyser extends DatabaseAnalyser {
.filter(x => x.sqlObjectType.trim() == 'P') .filter(x => x.sqlObjectType.trim() == 'P')
.map(row => ({ .map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: createObjectContentHash('procedures', row),
createSql: getCreateSql(row), createSql: getCreateSql(row),
parameters: prodceureToParameters[row.objectId], parameters: prodceureToParameters[row.objectId],
})); }));
@@ -213,14 +247,14 @@ class MsSqlAnalyser extends DatabaseAnalyser {
.filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim())) .filter(x => ['FN', 'IF', 'TF'].includes(x.sqlObjectType.trim()))
.map(row => ({ .map(row => ({
...row, ...row,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: createObjectContentHash('functions', row),
createSql: getCreateSql(row), createSql: getCreateSql(row),
parameters: functionToParameters[row.objectId], parameters: functionToParameters[row.objectId],
})); }));
const triggers = triggerRows.rows.map(row => ({ const triggers = triggerRows.rows.map(row => ({
objectId: `triggers:${row.objectId}`, objectId: `triggers:${row.objectId}`,
contentHash: row.modifyDate && row.modifyDate.toISOString(), contentHash: createObjectContentHash('triggers', row),
createSql: row.definition, createSql: row.definition,
triggerTiming: row.triggerTiming, triggerTiming: row.triggerTiming,
eventType: row.eventType, eventType: row.eventType,
@@ -241,17 +275,19 @@ class MsSqlAnalyser extends DatabaseAnalyser {
async _getFastSnapshot() { async _getFastSnapshot() {
const modificationsQueryData = await this.analyserQuery('modifications'); const modificationsQueryData = await this.analyserQuery('modifications');
const baseColumnsRows = await this.analyserQuery('baseColumns', ['tables']);
const baseColumns = baseColumnsRows.rows;
const tableSizes = await this.analyserQuery('tableSizes'); const tableSizes = await this.analyserQuery('tableSizes');
const res = DatabaseAnalyser.createEmptyStructure(); const res = DatabaseAnalyser.createEmptyStructure();
for (const item of modificationsQueryData.rows) { for (const item of modificationsQueryData.rows) {
const { type, objectId, modifyDate, schemaName, pureName } = item; const { type, objectId, schemaName, pureName } = item;
const field = objectTypeToField(type); const field = objectTypeToField(type);
if (!field || !res[field]) continue; if (!field || !res[field]) continue;
res[field].push({ res[field].push({
objectId, objectId,
contentHash: modifyDate && modifyDate.toISOString(), contentHash: createObjectContentHash(field, item, baseColumns),
schemaName, schemaName,
pureName, pureName,
}); });

View File

@@ -0,0 +1,11 @@
module.exports = `
select c.object_id as objectId,
ep.value as columnComment,
c.column_id as columnId
from sys.columns c
inner join sys.objects o on c.object_id = o.object_id
INNER JOIN sys.schemas u ON u.schema_id=o.schema_id
INNER JOIN sys.extended_properties ep on ep.major_id = c.object_id and ep.minor_id = c.column_id and ep.name = 'MS_Description'
where o.type IN ('U', 'V') and o.object_id =OBJECT_ID_CONDITION and u.name =SCHEMA_NAME_CONDITION
order by c.column_id
`;

View File

@@ -7,7 +7,8 @@ select c.name as columnName, t.name as dataType, c.object_id as objectId, c.is_i
col.NUMERIC_PRECISION as numericPrecision, col.NUMERIC_PRECISION as numericPrecision,
col.NUMERIC_SCALE as numericScale, col.NUMERIC_SCALE as numericScale,
-- TODO only if version >= 2008 -- TODO only if version >= 2008
c.is_sparse as isSparse c.is_sparse as isSparse,
ep.value as columnComment
from sys.columns c from sys.columns c
inner join sys.types t on c.system_type_id = t.system_type_id and c.user_type_id = t.user_type_id inner join sys.types t on c.system_type_id = t.system_type_id and c.user_type_id = t.user_type_id
inner join sys.objects o on c.object_id = o.object_id inner join sys.objects o on c.object_id = o.object_id
@@ -15,6 +16,7 @@ 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 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.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 left join sys.computed_columns m on m.object_id = c.object_id and m.column_id = c.column_id
left join sys.extended_properties ep on ep.major_id = c.object_id and ep.minor_id = c.column_id and ep.name = 'MS_Description'
where o.type = 'U' and o.object_id =OBJECT_ID_CONDITION and u.name =SCHEMA_NAME_CONDITION where o.type = 'U' and o.object_id =OBJECT_ID_CONDITION and u.name =SCHEMA_NAME_CONDITION
order by c.column_id order by c.column_id
`; `;

View File

@@ -13,6 +13,7 @@ const viewColumns = require('./viewColumns');
const indexes = require('./indexes'); const indexes = require('./indexes');
const indexcols = require('./indexcols'); const indexcols = require('./indexcols');
const triggers = require('./triggers'); const triggers = require('./triggers');
const baseColumns = require('./baseColumns');
module.exports = { module.exports = {
columns, columns,
@@ -30,4 +31,5 @@ module.exports = {
indexcols, indexcols,
tableSizes, tableSizes,
triggers, triggers,
baseColumns,
}; };

View File

@@ -1,8 +1,14 @@
module.exports = ` module.exports = `
select select
o.name as pureName, s.name as schemaName, o.object_id as objectId, o.name as pureName,
o.create_date as createDate, o.modify_date as modifyDate s.name as schemaName,
o.object_id as objectId,
o.create_date as createDate,
o.modify_date as modifyDate,
ep.value as objectComment
from sys.tables o from sys.tables o
inner join sys.schemas s on o.schema_id = s.schema_id inner join sys.schemas s on o.schema_id = s.schema_id
where o.object_id =OBJECT_ID_CONDITION and s.name =SCHEMA_NAME_CONDITION left join sys.extended_properties ep on ep.major_id = o.object_id
`; and ep.minor_id = 0
and ep.name = 'MS_Description'
where o.object_id =OBJECT_ID_CONDITION and s.name =SCHEMA_NAME_CONDITION`;

View File

@@ -124,8 +124,110 @@ class MsSqlDumper extends SqlDumper {
this.putCmd("^execute sp_rename '%f.%i', '%s', 'COLUMN'", column, column.columnName, newcol); this.putCmd("^execute sp_rename '%f.%i', '%s', 'COLUMN'", column, column.columnName, newcol);
} }
/**
* @param {import('dbgate-types').TableInfo} table
*/
dropTableCommentIfExists(table) {
const { schemaName, pureName } = table;
const fullName = `${schemaName && schemaName + '.'}${pureName}`;
this.put('&>^if ^exists (&n');
this.put('&>^select 1 ^from sys.extended_properties&n');
this.put("^where major_id = OBJECT_ID('%s')&n", fullName);
this.put('^and minor_id = 0&n');
this.put("^and name = N'MS_Description'&<&<&n");
this.put(')&n');
this.put('&>^begin&n');
this.put('&>^exec sp_dropextendedproperty&n');
this.put("@name = N'MS_Description',&n");
this.put("@level0type = N'SCHEMA', @level0name = '%s',&n", schemaName);
this.put("@level1type = N'TABLE', @level1name = '%s'&<&n", pureName);
this.put('^end');
this.endCommand();
}
/**
* @param {import('dbgate-types').TableInfo} table
*/
createTableComment(table) {
const { schemaName, pureName, objectComment } = table;
if (!objectComment) return;
this.put('&>^exec sp_addextendedproperty&n');
this.put("@name = N'MS_Description', @value = N'%s',&n", objectComment);
this.put("@level0type = N'SCHEMA', @level0name = '%s',&n", schemaName || 'dbo');
this.put("@level1type = N'TABLE', @level1name = '%s&<'", pureName);
this.endCommand();
}
/**
* @param {import('dbgate-types').ColumnInfo} oldcol
* @param {import('dbgate-types').ColumnInfo} newcol
*/
changeColumnComment(oldcol, newcol) {
if (oldcol.columnComment === newcol.columnComment) return;
if (oldcol.columnComment) this.dropColumnCommentIfExists(newcol);
if (newcol.columnComment) this.createColumnComment(newcol);
}
/**
* @param {import('dbgate-types').ColumnInfo} column
*/
dropColumnCommentIfExists(column) {
const { schemaName, columnName, pureName } = column;
const fullName = `${schemaName && schemaName + '.'}${pureName}`;
this.put('&>^if ^exists (&n');
this.put('&>^select 1 ^from sys.extended_properties&n');
this.put("^where major_id = OBJECT_ID('%s')&n", fullName);
this.put(
"^and minor_id = (^select column_id ^from sys.columns ^where object_id = OBJECT_ID('%s') ^and name = '%s')&n",
fullName,
columnName
);
this.put("^and name = N'MS_Description'&<&<&n");
this.put(')&n');
this.put('&>^begin&n');
this.put('&>^exec sp_dropextendedproperty&n');
this.put("@name = N'MS_Description',&n");
this.put("@level0type = N'SCHEMA', @level0name = '%s',&n", schemaName);
this.put("@level1type = N'TABLE', @level1name = '%s',&n", pureName);
this.put("@level2type = N'COLUMN', @level2name = '%s'&<&n", columnName);
this.put('^end');
this.endCommand();
}
/**
* @param {import('dbgate-types').ColumnInfo} column
*/
createColumnComment(column) {
const { schemaName, columnName, pureName, columnComment } = column;
if (!columnComment) return;
this.put('&>^exec sp_addextendedproperty&n');
this.put("@name = N'MS_Description', ");
this.put(`@value = N'%s',&n`, columnComment);
this.put("@level0type = N'SCHEMA', @level0name = '%s',&n", schemaName);
this.put("@level1type = N'TABLE', @level1name = '%s',&n", pureName);
this.put("@level2type = N'COLUMN', @level2name = '%s&<'", columnName);
this.endCommand();
}
/**
* @param {import('dbgate-types').TableInfo} table
*/
createTable(table) {
super.createTable(table);
for (const column of table.columns || []) {
this.createColumnComment(column);
}
}
changeColumn(oldcol, newcol, constraints) { changeColumn(oldcol, newcol, constraints) {
if (testEqualColumns(oldcol, newcol, false, false)) { if (testEqualColumns(oldcol, newcol, false, false, { ignoreComments: true })) {
this.dropDefault(oldcol); this.dropDefault(oldcol);
if (oldcol.columnName != newcol.columnName) this.renameColumn(oldcol, newcol.columnName); if (oldcol.columnName != newcol.columnName) this.renameColumn(oldcol, newcol.columnName);
this.createDefault(newcol); this.createDefault(newcol);
@@ -140,6 +242,8 @@ class MsSqlDumper extends SqlDumper {
this.endCommand(); this.endCommand();
this.createDefault(newcol); this.createDefault(newcol);
} }
this.changeColumnComment(oldcol, newcol);
} }
specialColumnOptions(column) { specialColumnOptions(column) {
@@ -163,6 +267,44 @@ class MsSqlDumper extends SqlDumper {
this.put('^select ^scope_identity()'); this.put('^select ^scope_identity()');
} }
/**
* @param {import('dbgate-types').TableInfo} table
*/
tableOptions(table) {
this.endCommand();
const options = this.driver?.dialect?.getTableFormOptions?.('sqlCreateTable') || [];
for (const option of options) {
const { name, sqlFormatString } = option;
const value = table[name];
if (name == 'objectComment') {
this.createTableComment(table);
return;
}
if (value) {
this.put('&n');
this.put(sqlFormatString, value);
}
}
}
/**
* @param {import('dbgate-types').TableInfo} table
* @param {string} optionName
* @param {string} optionValue
*/
setTableOption(table, optionName, optionValue) {
if (optionName == 'objectComment') {
this.dropTableCommentIfExists(table);
if (optionValue) this.createTableComment(table);
return;
}
super.setTableOption(table, optionName, optionValue);
}
callableTemplate(func) { callableTemplate(func) {
const putParameters = (parameters, delimiter) => { const putParameters = (parameters, delimiter) => {
this.putCollection(delimiter, parameters || [], param => { this.putCollection(delimiter, parameters || [], param => {
@@ -207,8 +349,8 @@ MsSqlDumper.prototype.changeProcedureSchema = MsSqlDumper.prototype.changeObject
MsSqlDumper.prototype.renameFunction = MsSqlDumper.prototype.renameObject; MsSqlDumper.prototype.renameFunction = MsSqlDumper.prototype.renameObject;
MsSqlDumper.prototype.changeFunctionSchema = MsSqlDumper.prototype.changeObjectSchema; MsSqlDumper.prototype.changeFunctionSchema = MsSqlDumper.prototype.changeObjectSchema;
MsSqlDumper.prototype.renameTrigger = MsSqlDumper.prototype.renameObject; MsSqlDumper.prototype.renameTrigger = MsSqlDumper.prototype.renameObject;
MsSqlDumper.prototype.changeTriggerSchema = MsSqlDumper.prototype.changeObjectSchema; MsSqlDumper.prototype.changeTriggerSchema = MsSqlDumper.prototype.changeObjectSchema;
MsSqlDumper.prototype.renameTable = MsSqlDumper.prototype.renameObject; MsSqlDumper.prototype.renameTable = MsSqlDumper.prototype.renameObject;

View File

@@ -45,10 +45,13 @@ const dialect = {
namedDefaultConstraint: true, namedDefaultConstraint: true,
columnProperties: { columnProperties: {
columnComment: true,
isSparse: true, isSparse: true,
isPersisted: true, isPersisted: true,
}, },
safeCommentChanges: true,
predefinedDataTypes: [ predefinedDataTypes: [
'bigint', 'bigint',
'bit', 'bit',
@@ -111,6 +114,18 @@ const dialect = {
}; };
} }
}, },
getTableFormOptions(intent) {
return [
{
type: 'text',
label: 'Comment',
name: 'objectComment',
sqlFormatString: '^comment = %v',
allowEmptyValue: true,
},
];
},
}; };
/** @type {import('dbgate-types').EngineDriver} */ /** @type {import('dbgate-types').EngineDriver} */