diff --git a/packages/types/dialect.d.ts b/packages/types/dialect.d.ts index 017b6f98b..da85ac967 100644 --- a/packages/types/dialect.d.ts +++ b/packages/types/dialect.d.ts @@ -74,6 +74,12 @@ export interface SqlDialect { predefinedDataTypes: string[]; + columnProperties?: { + columnName?: boolean; + isSparse?: true; + isPersisted?: true; + }; + // create sql-tree expression createColumnViewExpression( columnName: string, diff --git a/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js b/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js index 32dace94d..17887761a 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js +++ b/plugins/dbgate-plugin-mssql/src/backend/sql/tables.js @@ -1,8 +1,14 @@ 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.name as pureName, + 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 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`; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js index 00f052544..e15a705f5 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/MsSqlDumper.js @@ -124,46 +124,98 @@ class MsSqlDumper extends SqlDumper { 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); + this.put("@level1type = N'TABLE', @level1name = '%s&<'", pureName); + this.endCommand(); + } + + /** + * @param {import('dbgate-types').TableInfo} table + */ + createColumnComment(table) { + const { schemaName, pureName, objectComment } = table; + if (!objectComment) return; + + this.put('&>^exec sp_addextendedproperty&n'); + this.put(`@value = N'%s',&n`, objectComment); + this.put("@level0type = N'SCHEMA', @level0name = '%s',&n", schemaName); + this.put("@level1type = N'TABLE', @level1name = '%s'&n&<", pureName); + this.endCommand(); + } + /** * @param {import('dbgate-types').ColumnInfo} oldcol * @param {import('dbgate-types').ColumnInfo} newcol */ - changeColumnDescription(oldcol, newcol) { + changeColumnComment(oldcol, newcol) { if (oldcol.columnComment == newcol.columnComment) return; if (oldcol.columnComment && !newcol.columnComment) { - this.dropColumnDescription(newcol); + this.dropColumnComment(newcol); } else { - this.dropColumnDescription(newcol); - this.createColumnDescription(newcol); + this.dropColumnComment(newcol); + this.createColumnComment(newcol); } } /** * @param {import('dbgate-types').ColumnInfo} column */ - dropColumnDescription(column) { + dropColumnComment(column) { const { schemaName, columnName, pureName } = column; - this.put('^exec sp_dropextendedproperty&n'); + this.put('&>^exec sp_dropextendedproperty&n'); this.put("@name = N'MS_Description',"); 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.put("@level2type = N'COLUMN', @level2name = '%s'&<", columnName); this.endCommand(); } /** * @param {import('dbgate-types').ColumnInfo} column */ - createColumnDescription(column) { + createColumnComment(column) { const { schemaName, columnName, pureName, columnComment } = column; + if (!columnComment) return; - this.put('^exec sp_addextendedproperty&n'); + 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.put("@level2type = N'COLUMN', @level2name = '%s&<'", columnName); this.endCommand(); } @@ -184,7 +236,7 @@ class MsSqlDumper extends SqlDumper { this.createDefault(newcol); } - this.changeColumnDescription(oldcol, newcol); + this.changeColumnComment(oldcol, newcol); } specialColumnOptions(column) { @@ -208,6 +260,21 @@ class MsSqlDumper extends SqlDumper { this.put('^select ^scope_identity()'); } + /** + * @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) { const putParameters = (parameters, delimiter) => { this.putCollection(delimiter, parameters || [], param => { diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index 309cfba19..91d36b12d 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -112,6 +112,18 @@ const dialect = { }; } }, + + getTableFormOptions(intent) { + return [ + { + type: 'text', + label: 'Comment', + name: 'objectComment', + sqlFormatString: '^comment = %v', + allowEmptyValue: true, + }, + ]; + }, }; /** @type {import('dbgate-types').EngineDriver} */