Compare commits
12 Commits
master
...
v6.6.1-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f2d94069b | ||
|
|
ba303c5b41 | ||
|
|
74c4586be3 | ||
|
|
d4e8d1026d | ||
|
|
3d6722ebfa | ||
|
|
2d28046149 | ||
|
|
0e91437e59 | ||
|
|
c67ca42f57 | ||
|
|
c391a675f0 | ||
|
|
d5d182a9db | ||
|
|
63de984d76 | ||
|
|
8779285408 |
@@ -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(
|
||||
createEnginesColumnsSource(engines.filter(x => !x.skipDropColumn)).filter(
|
||||
([_label, col, engine]) => !engine.skipPkDrop || !col.endsWith('_pk')
|
||||
|
||||
@@ -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]))(
|
||||
'Table with index - %s',
|
||||
testWrapper(async (conn, driver, engine) => {
|
||||
|
||||
@@ -443,6 +443,8 @@ const sqlServerEngine = {
|
||||
supportSchemas: true,
|
||||
supportRenameSqlObject: true,
|
||||
defaultSchemaName: 'dbo',
|
||||
supportTableComments: true,
|
||||
supportColumnComments: true,
|
||||
// skipSeparateSchemas: true,
|
||||
triggers: [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"private": true,
|
||||
"version": "6.6.1-beta.7",
|
||||
"version": "6.6.1-beta.16",
|
||||
"name": "dbgate-all",
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
|
||||
6
packages/types/dialect.d.ts
vendored
6
packages/types/dialect.d.ts
vendored
@@ -74,6 +74,12 @@ export interface SqlDialect {
|
||||
|
||||
predefinedDataTypes: string[];
|
||||
|
||||
columnProperties?: {
|
||||
columnName?: boolean;
|
||||
isSparse?: true;
|
||||
isPersisted?: true;
|
||||
};
|
||||
|
||||
// create sql-tree expression
|
||||
createColumnViewExpression(
|
||||
columnName: string,
|
||||
|
||||
3
packages/types/test-engines.d.ts
vendored
3
packages/types/test-engines.d.ts
vendored
@@ -56,6 +56,9 @@ export type TestEngineInfo = {
|
||||
|
||||
useTextTypeForStrings?: boolean;
|
||||
|
||||
supportTableComments?: boolean;
|
||||
supportColumnComments?: boolean;
|
||||
|
||||
supportRenameSqlObject?: boolean;
|
||||
supportSchemas?: boolean;
|
||||
|
||||
|
||||
@@ -46,11 +46,11 @@
|
||||
"rollup-plugin-copy": "^3.3.0",
|
||||
"rollup-plugin-css-only": "^3.1.0",
|
||||
"rollup-plugin-livereload": "^2.0.0",
|
||||
"rollup-plugin-svelte": "^7.0.0",
|
||||
"rollup-plugin-terser": "^7.0.0",
|
||||
"rollup-plugin-svelte": "^7.2.2",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"sirv-cli": "^1.0.0",
|
||||
"sql-formatter": "^3.1.0",
|
||||
"svelte": "^3.46.4",
|
||||
"svelte": "^5.37.3",
|
||||
"svelte-check": "^1.0.0",
|
||||
"svelte-markdown": "^0.1.4",
|
||||
"svelte-preprocess": "^4.9.5",
|
||||
|
||||
@@ -88,6 +88,7 @@ export default [
|
||||
compilerOptions: {
|
||||
// enable run-time checks when not in production
|
||||
dev: !production,
|
||||
errorMode: 'warn',
|
||||
},
|
||||
onwarn: (warning, handler) => {
|
||||
const ignoreWarnings = [
|
||||
|
||||
@@ -13,20 +13,22 @@
|
||||
|
||||
<div>
|
||||
<table>
|
||||
<tr>
|
||||
<td
|
||||
use:resizeObserver={true}
|
||||
on:resize={e => {
|
||||
// @ts-ignore
|
||||
$dataGridRowHeight = e.detail.height + 1;
|
||||
}}
|
||||
>
|
||||
title
|
||||
<InlineButton square>
|
||||
<FontIcon icon="icon chevron-down" />
|
||||
</InlineButton>
|
||||
</td>
|
||||
</tr>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
use:resizeObserver={true}
|
||||
on:resize={e => {
|
||||
// @ts-ignore
|
||||
$dataGridRowHeight = e.detail.height + 1;
|
||||
}}
|
||||
>
|
||||
title
|
||||
<InlineButton square>
|
||||
<FontIcon icon="icon chevron-down" />
|
||||
</InlineButton>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ const Analyser = require('./Analyser');
|
||||
const isPromise = require('is-promise');
|
||||
const { MongoClient, ObjectId, AbstractCursor, Long } = require('mongodb');
|
||||
const { EJSON } = require('bson');
|
||||
const { NodeDriverServiceProvider } = require('@mongosh/service-provider-node-driver');
|
||||
const { ElectronRuntime } = require('@mongosh/browser-runtime-electron');
|
||||
const { serializeJsTypesForJsonStringify, deserializeJsTypesFromJsonParse } = require('dbgate-tools');
|
||||
const createBulkInsertStream = require('./createBulkInsertStream');
|
||||
const {
|
||||
@@ -30,24 +32,10 @@ function serializeMongoData(row) {
|
||||
);
|
||||
}
|
||||
|
||||
async function readCursor(cursor, options) {
|
||||
options.recordset({ __isDynamicStructure: true });
|
||||
await cursor.forEach((row) => {
|
||||
options.row(serializeMongoData(row));
|
||||
});
|
||||
}
|
||||
|
||||
function deserializeMongoData(value) {
|
||||
return deserializeJsTypesFromJsonParse(EJSON.deserialize(value));
|
||||
}
|
||||
|
||||
function findArrayResult(resValue) {
|
||||
if (!_.isPlainObject(resValue)) return null;
|
||||
const arrays = _.values(resValue).filter((x) => _.isArray(x));
|
||||
if (arrays.length == 1) return arrays[0];
|
||||
return null;
|
||||
}
|
||||
|
||||
async function getScriptableDb(dbhan) {
|
||||
const db = dbhan.getDatabase();
|
||||
db.getCollection = (name) => db.collection(name);
|
||||
@@ -123,7 +111,7 @@ const driver = {
|
||||
};
|
||||
},
|
||||
// @ts-ignore
|
||||
async query(dbhan, sql) {
|
||||
async query(_dbhan, _sql) {
|
||||
return {
|
||||
rows: [],
|
||||
columns: [],
|
||||
@@ -158,7 +146,7 @@ const driver = {
|
||||
if (isPromise(res)) await res;
|
||||
}
|
||||
},
|
||||
async operation(dbhan, operation, options) {
|
||||
async operation(dbhan, operation, _options) {
|
||||
const { type } = operation;
|
||||
switch (type) {
|
||||
case 'createCollection':
|
||||
@@ -357,7 +345,7 @@ const driver = {
|
||||
const db = await getScriptableDb(dbhan);
|
||||
await db.command({ profile: old.was, slowms: old.slowms });
|
||||
},
|
||||
async readQuery(dbhan, sql, structure) {
|
||||
async readQuery(dbhan, sql, _structure) {
|
||||
try {
|
||||
const json = JSON.parse(sql);
|
||||
if (json && json.pureName) {
|
||||
@@ -498,7 +486,7 @@ const driver = {
|
||||
res.replaced.push(resdoc._id);
|
||||
}
|
||||
} else {
|
||||
const set = deserializeMongoData(_.pickBy(update.fields, (v, k) => !v?.$$undefined$$));
|
||||
const set = deserializeMongoData(_.pickBy(update.fields, (v, _k) => !v?.$$undefined$$));
|
||||
const unset = _.fromPairs(
|
||||
Object.keys(update.fields)
|
||||
.filter((k) => update.fields[k]?.$$undefined$$)
|
||||
@@ -581,7 +569,7 @@ const driver = {
|
||||
}
|
||||
},
|
||||
|
||||
readJsonQuery(dbhan, select, structure) {
|
||||
readJsonQuery(dbhan, select, _structure) {
|
||||
const { collection, condition, sort } = select;
|
||||
|
||||
const db = dbhan.getDatabase();
|
||||
|
||||
@@ -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`;
|
||||
|
||||
@@ -124,49 +124,95 @@ 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 || 'dbo');
|
||||
this.put("@level1type = N'TABLE', @level1name = '%s&<'", pureName);
|
||||
this.endCommand();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('dbgate-types').ColumnInfo} oldcol
|
||||
* @param {import('dbgate-types').ColumnInfo} newcol
|
||||
*/
|
||||
changeColumnDescription(oldcol, newcol) {
|
||||
if (oldcol.columnComment == newcol.columnComment) return;
|
||||
if (oldcol.columnComment && !newcol.columnComment) {
|
||||
this.dropColumnDescription(newcol);
|
||||
} else {
|
||||
this.dropColumnDescription(newcol);
|
||||
this.createColumnDescription(newcol);
|
||||
}
|
||||
changeColumnComment(oldcol, newcol) {
|
||||
if (oldcol.columnComment === newcol.columnComment) return;
|
||||
|
||||
if (oldcol.columnComment) this.dropColumnComment(newcol);
|
||||
if (newcol.columnComment) 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("@name = N'MS_Description',");
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('dbgate-types').TableInfo} table
|
||||
*/
|
||||
createTable(table) {
|
||||
super.createTable(table);
|
||||
|
||||
for (const column of table.columns || []) {
|
||||
this.createColumnComment(column);
|
||||
}
|
||||
}
|
||||
|
||||
changeColumn(oldcol, newcol, constraints) {
|
||||
if (testEqualColumns(oldcol, newcol, false, false, { ignoreComments: true })) {
|
||||
this.dropDefault(oldcol);
|
||||
@@ -184,7 +230,7 @@ class MsSqlDumper extends SqlDumper {
|
||||
this.createDefault(newcol);
|
||||
}
|
||||
|
||||
this.changeColumnDescription(oldcol, newcol);
|
||||
this.changeColumnComment(oldcol, newcol);
|
||||
}
|
||||
|
||||
specialColumnOptions(column) {
|
||||
@@ -208,6 +254,44 @@ class MsSqlDumper extends SqlDumper {
|
||||
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) {
|
||||
const putParameters = (parameters, delimiter) => {
|
||||
this.putCollection(delimiter, parameters || [], param => {
|
||||
|
||||
@@ -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} */
|
||||
|
||||
Reference in New Issue
Block a user