mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
!110 feat: 支持各源数据库导出sql,数据库迁移部分bug修复
* feat: 各源数据库导出 * fix: 数据库迁移 bug修复
This commit is contained in:
@@ -472,6 +472,9 @@ export class DbInst {
|
|||||||
|
|
||||||
// 初始化所有列信息,完善需要显示的列类型,包含长度等,如varchar(20)
|
// 初始化所有列信息,完善需要显示的列类型,包含长度等,如varchar(20)
|
||||||
static initColumns(columns: any[]) {
|
static initColumns(columns: any[]) {
|
||||||
|
if (!columns) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (let col of columns) {
|
for (let col of columns) {
|
||||||
if (col.charMaxLength > 0) {
|
if (col.charMaxLength > 0) {
|
||||||
col.showDataType = `${col.dataType}(${col.charMaxLength})`;
|
col.showDataType = `${col.dataType}(${col.charMaxLength})`;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DuplicateStrategy,
|
DuplicateStrategy,
|
||||||
EditorCompletion,
|
EditorCompletion,
|
||||||
EditorCompletionItem,
|
EditorCompletionItem,
|
||||||
|
QuoteEscape,
|
||||||
IndexDefinition,
|
IndexDefinition,
|
||||||
RowDefinition,
|
RowDefinition,
|
||||||
sqlColumnType,
|
sqlColumnType,
|
||||||
@@ -523,7 +524,7 @@ class DMDialect implements DbDialect {
|
|||||||
}
|
}
|
||||||
// 列注释
|
// 列注释
|
||||||
if (item.remark) {
|
if (item.remark) {
|
||||||
columCommentSql += ` comment on column "${data.tableName}"."${item.name}" is '${item.remark}'; `;
|
columCommentSql += ` comment on column "${data.tableName}"."${item.name}" is '${QuoteEscape(item.remark)}'; `;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 建表
|
// 建表
|
||||||
@@ -534,7 +535,7 @@ class DMDialect implements DbDialect {
|
|||||||
);`;
|
);`;
|
||||||
// 表注释
|
// 表注释
|
||||||
if (data.tableComment) {
|
if (data.tableComment) {
|
||||||
tableCommentSql = ` comment on table "${data.tableName}" is '${data.tableComment}'; `;
|
tableCommentSql = ` comment on table "${data.tableName}" is '${QuoteEscape(data.tableComment)}'; `;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSql + tableCommentSql + columCommentSql;
|
return createSql + tableCommentSql + columCommentSql;
|
||||||
@@ -569,7 +570,7 @@ class DMDialect implements DbDialect {
|
|||||||
changeData.add.forEach((a) => {
|
changeData.add.forEach((a) => {
|
||||||
modifySql += `ALTER TABLE ${dbTable} add COLUMN ${this.genColumnBasicSql(a)};`;
|
modifySql += `ALTER TABLE ${dbTable} add COLUMN ${this.genColumnBasicSql(a)};`;
|
||||||
if (a.remark) {
|
if (a.remark) {
|
||||||
commentSql += `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${a.remark}';`;
|
commentSql += `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${QuoteEscape(a.remark)}';`;
|
||||||
}
|
}
|
||||||
if (a.pri) {
|
if (a.pri) {
|
||||||
priArr.add(`"${a.name}"`);
|
priArr.add(`"${a.name}"`);
|
||||||
@@ -579,7 +580,7 @@ class DMDialect implements DbDialect {
|
|||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
if (changeData.upd.length > 0) {
|
||||||
changeData.upd.forEach((a) => {
|
changeData.upd.forEach((a) => {
|
||||||
let cmtSql = `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${a.remark}';`;
|
let cmtSql = `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${QuoteEscape(a.remark)}';`;
|
||||||
if (a.remark && a.oldName === a.name) {
|
if (a.remark && a.oldName === a.name) {
|
||||||
commentSql += cmtSql;
|
commentSql += cmtSql;
|
||||||
}
|
}
|
||||||
@@ -675,7 +676,7 @@ class DMDialect implements DbDialect {
|
|||||||
}
|
}
|
||||||
if (tableData.oldTableComment !== tableData.tableComment) {
|
if (tableData.oldTableComment !== tableData.tableComment) {
|
||||||
let baseTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.tableName)}`;
|
let baseTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.tableName)}`;
|
||||||
sql += `COMMENT ON TABLE ${baseTable} IS '${tableData.tableComment}';`;
|
sql += `COMMENT ON TABLE ${baseTable} IS '${QuoteEscape(tableData.tableComment)}';`;
|
||||||
}
|
}
|
||||||
return sql;
|
return sql;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,6 +262,18 @@ export const getDbDialect = (dbType?: string): DbDialect => {
|
|||||||
return dbType2DialectMap.get(dbType!) || mysqlDialect;
|
return dbType2DialectMap.get(dbType!) || mysqlDialect;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 引号转义,多用于sql注释转义,防止拼接sql报错,如: comment xx is '注''释' 最终注释文本为: 注'释
|
||||||
|
* @author liuzongyang
|
||||||
|
* @since 2024/3/22 08:23
|
||||||
|
*/
|
||||||
|
export const QuoteEscape = (str: string): string => {
|
||||||
|
if (!str) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return str.replace(/'/g, "''");
|
||||||
|
};
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
console.log('init register db dialect');
|
console.log('init register db dialect');
|
||||||
registerDbDialect(DbType.mysql, mysqlDialect);
|
registerDbDialect(DbType.mysql, mysqlDialect);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DuplicateStrategy,
|
DuplicateStrategy,
|
||||||
EditorCompletion,
|
EditorCompletion,
|
||||||
EditorCompletionItem,
|
EditorCompletionItem,
|
||||||
|
QuoteEscape,
|
||||||
IndexDefinition,
|
IndexDefinition,
|
||||||
RowDefinition,
|
RowDefinition,
|
||||||
} from './index';
|
} from './index';
|
||||||
@@ -225,7 +226,7 @@ class MssqlDialect implements DbDialect {
|
|||||||
item.name && fields.push(this.genColumnBasicSql(item));
|
item.name && fields.push(this.genColumnBasicSql(item));
|
||||||
item.remark &&
|
item.remark &&
|
||||||
fieldComments.push(
|
fieldComments.push(
|
||||||
`EXECUTE sp_addextendedproperty N'MS_Description', N'${item.remark}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}', N'COLUMN', N'${item.name}'`
|
`EXECUTE sp_addextendedproperty N'MS_Description', N'${QuoteEscape(item.remark)}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}', N'COLUMN', N'${item.name}'`
|
||||||
);
|
);
|
||||||
if (item.pri) {
|
if (item.pri) {
|
||||||
pks.push(`${this.quoteIdentifier(item.name)}`);
|
pks.push(`${this.quoteIdentifier(item.name)}`);
|
||||||
@@ -244,7 +245,7 @@ class MssqlDialect implements DbDialect {
|
|||||||
|
|
||||||
// 表注释
|
// 表注释
|
||||||
if (data.tableComment) {
|
if (data.tableComment) {
|
||||||
createTable += ` EXECUTE sp_addextendedproperty N'MS_Description', N'${data.tableComment}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}';`;
|
createTable += ` EXECUTE sp_addextendedproperty N'MS_Description', N'${QuoteEscape(data.tableComment)}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}';`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createTable + createIndexSql + fieldComments.join(';');
|
return createTable + createIndexSql + fieldComments.join(';');
|
||||||
@@ -268,7 +269,7 @@ class MssqlDialect implements DbDialect {
|
|||||||
sql.push(` CREATE ${a.unique ? 'UNIQUE' : ''} NONCLUSTERED INDEX ${this.quoteIdentifier(a.indexName)} on ${baseTable} (${columnNames.join(',')})`);
|
sql.push(` CREATE ${a.unique ? 'UNIQUE' : ''} NONCLUSTERED INDEX ${this.quoteIdentifier(a.indexName)} on ${baseTable} (${columnNames.join(',')})`);
|
||||||
if (a.indexComment) {
|
if (a.indexComment) {
|
||||||
indexComment.push(
|
indexComment.push(
|
||||||
`EXECUTE sp_addextendedproperty N'MS_Description', N'${a.indexComment}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}', N'INDEX', N'${a.indexName}'`
|
`EXECUTE sp_addextendedproperty N'MS_Description', N'${QuoteEscape(a.indexComment)}', N'SCHEMA', N'${schema}', N'TABLE', N'${data.tableName}', N'INDEX', N'${a.indexName}'`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -306,7 +307,7 @@ class MssqlDialect implements DbDialect {
|
|||||||
addArr.push(` ALTER TABLE ${baseTable} ADD ${this.genColumnBasicSql(a)}`);
|
addArr.push(` ALTER TABLE ${baseTable} ADD ${this.genColumnBasicSql(a)}`);
|
||||||
if (a.remark) {
|
if (a.remark) {
|
||||||
addCommentArr.push(
|
addCommentArr.push(
|
||||||
`EXECUTE sp_addextendedproperty N'MS_Description', N'${a.remark}', N'SCHEMA', N'${schema}', N'TABLE', N'${tableName}', N'COLUMN', N'${a.name}'`
|
`EXECUTE sp_addextendedproperty N'MS_Description', N'${QuoteEscape(a.remark)}', N'SCHEMA', N'${schema}', N'TABLE', N'${tableName}', N'COLUMN', N'${a.name}'`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -315,7 +316,7 @@ class MssqlDialect implements DbDialect {
|
|||||||
if (changeData.upd.length > 0) {
|
if (changeData.upd.length > 0) {
|
||||||
changeData.upd.forEach((a) => {
|
changeData.upd.forEach((a) => {
|
||||||
if (a.oldName && a.name !== a.oldName) {
|
if (a.oldName && a.name !== a.oldName) {
|
||||||
renameArr.push(` EXEC sp_rename '${baseTable}.${this.quoteIdentifier(a.oldName)}', '${a.name}', 'COLUMN' `);
|
renameArr.push(` EXEC sp_rename '${baseTable}.${this.quoteIdentifier(a.oldName)}', '${QuoteEscape(a.name)}', 'COLUMN' `);
|
||||||
} else {
|
} else {
|
||||||
updArr.push(` ALTER TABLE ${baseTable} ALTER COLUMN ${this.genColumnBasicSql(a)} `);
|
updArr.push(` ALTER TABLE ${baseTable} ALTER COLUMN ${this.genColumnBasicSql(a)} `);
|
||||||
}
|
}
|
||||||
@@ -325,13 +326,13 @@ class MssqlDialect implements DbDialect {
|
|||||||
'TABLE', N'${tableName}',
|
'TABLE', N'${tableName}',
|
||||||
'COLUMN', N'${a.name}')) > 0)
|
'COLUMN', N'${a.name}')) > 0)
|
||||||
EXEC sp_updateextendedproperty
|
EXEC sp_updateextendedproperty
|
||||||
'MS_Description', N'${a.remark}',
|
'MS_Description', N'${QuoteEscape(a.remark)}',
|
||||||
'SCHEMA', N'${schema}',
|
'SCHEMA', N'${schema}',
|
||||||
'TABLE', N'${tableName}',
|
'TABLE', N'${tableName}',
|
||||||
'COLUMN', N'${a.name}'
|
'COLUMN', N'${a.name}'
|
||||||
ELSE
|
ELSE
|
||||||
EXEC sp_addextendedproperty
|
EXEC sp_addextendedproperty
|
||||||
'MS_Description', N'${a.remark}',
|
'MS_Description', N'${QuoteEscape(a.remark)}',
|
||||||
'SCHEMA', N'${schema}',
|
'SCHEMA', N'${schema}',
|
||||||
'TABLE', N'${tableName}',
|
'TABLE', N'${tableName}',
|
||||||
'COLUMN',N'${a.name}'`);
|
'COLUMN',N'${a.name}'`);
|
||||||
@@ -367,7 +368,7 @@ ELSE
|
|||||||
);
|
);
|
||||||
if (a.indexComment) {
|
if (a.indexComment) {
|
||||||
commentArr.push(
|
commentArr.push(
|
||||||
` EXEC sp_addextendedproperty N'MS_Description', N'${a.indexComment}', N'SCHEMA', N'${schema}', N'TABLE', N'${tableName}', N'INDEX', N'${a.indexName}' `
|
` EXEC sp_addextendedproperty N'MS_Description', N'${QuoteEscape(a.indexComment)}', N'SCHEMA', N'${schema}', N'TABLE', N'${tableName}', N'INDEX', N'${a.indexName}' `
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -413,7 +414,7 @@ ELSE
|
|||||||
|
|
||||||
if (tableData.oldTableComment !== tableData.tableComment) {
|
if (tableData.oldTableComment !== tableData.tableComment) {
|
||||||
// 转义注释中的单引号和换行符
|
// 转义注释中的单引号和换行符
|
||||||
let tableComment = tableData.tableComment.replaceAll(/'/g, '').replaceAll(/[\r\n]/g, ' ');
|
let tableComment = tableData.tableComment.replaceAll(/'/g, "'").replaceAll(/[\r\n]/g, ' ');
|
||||||
sql += `IF ((SELECT COUNT(*) FROM fn_listextendedproperty('MS_Description',
|
sql += `IF ((SELECT COUNT(*) FROM fn_listextendedproperty('MS_Description',
|
||||||
'SCHEMA', N'${schema}',
|
'SCHEMA', N'${schema}',
|
||||||
'TABLE', N'${tableData.tableName}', NULL, NULL)) > 0)
|
'TABLE', N'${tableData.tableName}', NULL, NULL)) > 0)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DuplicateStrategy,
|
DuplicateStrategy,
|
||||||
EditorCompletion,
|
EditorCompletion,
|
||||||
EditorCompletionItem,
|
EditorCompletionItem,
|
||||||
|
QuoteEscape,
|
||||||
IndexDefinition,
|
IndexDefinition,
|
||||||
RowDefinition,
|
RowDefinition,
|
||||||
} from './index';
|
} from './index';
|
||||||
@@ -208,7 +209,7 @@ class MysqlDialect implements DbDialect {
|
|||||||
let onUpdate = 'update_time' === cl.name ? ' ON UPDATE CURRENT_TIMESTAMP ' : '';
|
let onUpdate = 'update_time' === cl.name ? ' ON UPDATE CURRENT_TIMESTAMP ' : '';
|
||||||
return ` ${this.quoteIdentifier(cl.name)} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : 'NULL'} ${
|
return ` ${this.quoteIdentifier(cl.name)} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : 'NULL'} ${
|
||||||
cl.auto_increment ? 'AUTO_INCREMENT' : ''
|
cl.auto_increment ? 'AUTO_INCREMENT' : ''
|
||||||
} ${defVal} ${onUpdate} comment '${cl.remark || ''}' `;
|
} ${defVal} ${onUpdate} comment '${QuoteEscape(cl.remark)}' `;
|
||||||
}
|
}
|
||||||
getCreateTableSql(data: any): string {
|
getCreateTableSql(data: any): string {
|
||||||
// 创建表结构
|
// 创建表结构
|
||||||
@@ -224,14 +225,14 @@ class MysqlDialect implements DbDialect {
|
|||||||
return `CREATE TABLE ${data.tableName}
|
return `CREATE TABLE ${data.tableName}
|
||||||
( ${fields.join(',')}
|
( ${fields.join(',')}
|
||||||
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
||||||
) COMMENT='${data.tableComment}';`;
|
) COMMENT='${QuoteEscape(data.tableComment)}';`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getCreateIndexSql(data: any): string {
|
getCreateIndexSql(data: any): string {
|
||||||
// 创建索引
|
// 创建索引
|
||||||
let sql = `ALTER TABLE ${data.tableName}`;
|
let sql = `ALTER TABLE ${data.tableName}`;
|
||||||
data.indexs.res.forEach((a: any) => {
|
data.indexs.res.forEach((a: any) => {
|
||||||
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${QuoteEscape(a.indexComment)}',`;
|
||||||
});
|
});
|
||||||
return sql.substring(0, sql.length - 1) + ';';
|
return sql.substring(0, sql.length - 1) + ';';
|
||||||
}
|
}
|
||||||
@@ -312,9 +313,9 @@ class MysqlDialect implements DbDialect {
|
|||||||
sql += ',';
|
sql += ',';
|
||||||
}
|
}
|
||||||
addIndexs.forEach((a) => {
|
addIndexs.forEach((a) => {
|
||||||
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${
|
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${QuoteEscape(
|
||||||
a.indexComment
|
a.indexComment
|
||||||
}',`;
|
)}',`;
|
||||||
});
|
});
|
||||||
sql = sql.substring(0, sql.length - 1);
|
sql = sql.substring(0, sql.length - 1);
|
||||||
}
|
}
|
||||||
@@ -326,7 +327,7 @@ class MysqlDialect implements DbDialect {
|
|||||||
getModifyTableInfoSql(tableData: any): string {
|
getModifyTableInfoSql(tableData: any): string {
|
||||||
let sql = '';
|
let sql = '';
|
||||||
if (tableData.tableComment !== tableData.oldTableComment) {
|
if (tableData.tableComment !== tableData.oldTableComment) {
|
||||||
sql += `ALTER TABLE ${this.quoteIdentifier(tableData.db)}.${this.quoteIdentifier(tableData.oldTableName)} COMMENT '${tableData.tableComment}';`;
|
sql += `ALTER TABLE ${this.quoteIdentifier(tableData.db)}.${this.quoteIdentifier(tableData.oldTableName)} COMMENT '${QuoteEscape(tableData.tableComment)}';`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tableData.tableName !== tableData.oldTableName) {
|
if (tableData.tableName !== tableData.oldTableName) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DuplicateStrategy,
|
DuplicateStrategy,
|
||||||
EditorCompletion,
|
EditorCompletion,
|
||||||
EditorCompletionItem,
|
EditorCompletionItem,
|
||||||
|
QuoteEscape,
|
||||||
IndexDefinition,
|
IndexDefinition,
|
||||||
RowDefinition,
|
RowDefinition,
|
||||||
sqlColumnType,
|
sqlColumnType,
|
||||||
@@ -324,7 +325,7 @@ class OracleDialect implements DbDialect {
|
|||||||
item.name && fields.push(this.genColumnBasicSql(item, true));
|
item.name && fields.push(this.genColumnBasicSql(item, true));
|
||||||
// 列注释
|
// 列注释
|
||||||
if (item.remark) {
|
if (item.remark) {
|
||||||
columCommentSql += ` COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(item.name)} is '${item.remark}'; `;
|
columCommentSql += ` COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(item.name)} is '${QuoteEscape(item.remark)}'; `;
|
||||||
}
|
}
|
||||||
// 主键
|
// 主键
|
||||||
if (item.pri) {
|
if (item.pri) {
|
||||||
@@ -340,7 +341,7 @@ class OracleDialect implements DbDialect {
|
|||||||
createSql = `CREATE TABLE ${dbTable} ( ${fields.join(',')} ${prisql ? ',' + prisql : ''} ) ;`;
|
createSql = `CREATE TABLE ${dbTable} ( ${fields.join(',')} ${prisql ? ',' + prisql : ''} ) ;`;
|
||||||
// 表注释
|
// 表注释
|
||||||
if (data.tableComment) {
|
if (data.tableComment) {
|
||||||
tableCommentSql = ` COMMENT ON TABLE ${dbTable} is '${data.tableComment}'; `;
|
tableCommentSql = ` COMMENT ON TABLE ${dbTable} is '${QuoteEscape(data.tableComment)}'; `;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSql + tableCommentSql + columCommentSql;
|
return createSql + tableCommentSql + columCommentSql;
|
||||||
@@ -379,7 +380,7 @@ class OracleDialect implements DbDialect {
|
|||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
if (changeData.upd.length > 0) {
|
||||||
changeData.upd.forEach((a) => {
|
changeData.upd.forEach((a) => {
|
||||||
let commentSql = `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${a.remark}'`;
|
let commentSql = `COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} IS '${QuoteEscape(a.remark)}'`;
|
||||||
if (a.remark && a.oldName === a.name) {
|
if (a.remark && a.oldName === a.name) {
|
||||||
commentArr.push(commentSql);
|
commentArr.push(commentSql);
|
||||||
}
|
}
|
||||||
@@ -401,7 +402,7 @@ class OracleDialect implements DbDialect {
|
|||||||
changeData.add.forEach((a) => {
|
changeData.add.forEach((a) => {
|
||||||
modifyArr.push(` ADD (${this.genColumnBasicSql(a, false)})`);
|
modifyArr.push(` ADD (${this.genColumnBasicSql(a, false)})`);
|
||||||
if (a.remark) {
|
if (a.remark) {
|
||||||
commentArr.push(`COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} is '${a.remark}'`);
|
commentArr.push(`COMMENT ON COLUMN ${dbTable}.${this.quoteIdentifier(a.name)} is '${QuoteEscape(a.remark)}'`);
|
||||||
}
|
}
|
||||||
if (a.pri) {
|
if (a.pri) {
|
||||||
priArr.add(`"${a.name}"`);
|
priArr.add(`"${a.name}"`);
|
||||||
@@ -486,7 +487,7 @@ class OracleDialect implements DbDialect {
|
|||||||
let sql = '';
|
let sql = '';
|
||||||
if (tableData.tableComment != tableData.oldTableComment) {
|
if (tableData.tableComment != tableData.oldTableComment) {
|
||||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
||||||
sql = `COMMENT ON TABLE ${dbTable} is '${tableData.tableComment}';`;
|
sql = `COMMENT ON TABLE ${dbTable} is '${QuoteEscape(tableData.tableComment)}';`;
|
||||||
}
|
}
|
||||||
if (tableData.tableName != tableData.oldTableName) {
|
if (tableData.tableName != tableData.oldTableName) {
|
||||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
DuplicateStrategy,
|
DuplicateStrategy,
|
||||||
EditorCompletion,
|
EditorCompletion,
|
||||||
EditorCompletionItem,
|
EditorCompletionItem,
|
||||||
|
QuoteEscape,
|
||||||
IndexDefinition,
|
IndexDefinition,
|
||||||
RowDefinition,
|
RowDefinition,
|
||||||
sqlColumnType,
|
sqlColumnType,
|
||||||
@@ -283,7 +284,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
}
|
}
|
||||||
// 列注释
|
// 列注释
|
||||||
if (item.remark) {
|
if (item.remark) {
|
||||||
columCommentSql += ` comment on column ${data.tableName}.${item.name} is '${item.remark}'; `;
|
columCommentSql += ` comment on column ${data.tableName}.${item.name} is '${QuoteEscape(item.remark)}'; `;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 建表
|
// 建表
|
||||||
@@ -294,7 +295,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
);`;
|
);`;
|
||||||
// 表注释
|
// 表注释
|
||||||
if (data.tableComment) {
|
if (data.tableComment) {
|
||||||
tableCommentSql = ` comment on table ${data.tableName} is '${data.tableComment}'; `;
|
tableCommentSql = ` comment on table ${data.tableName} is '${QuoteEscape(data.tableComment)}'; `;
|
||||||
}
|
}
|
||||||
|
|
||||||
return createSql + tableCommentSql + columCommentSql;
|
return createSql + tableCommentSql + columCommentSql;
|
||||||
@@ -312,7 +313,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
||||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
||||||
if (a.indexComment) {
|
if (a.indexComment) {
|
||||||
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${a.indexComment}'`);
|
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${QuoteEscape(a.indexComment)}'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return sql.join(';');
|
return sql.join(';');
|
||||||
@@ -334,14 +335,14 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
changeData.add.forEach((a) => {
|
changeData.add.forEach((a) => {
|
||||||
modifySql += `alter table ${dbTable} add ${this.genColumnBasicSql(a)};`;
|
modifySql += `alter table ${dbTable} add ${this.genColumnBasicSql(a)};`;
|
||||||
if (a.remark) {
|
if (a.remark) {
|
||||||
commentSql += `comment on column ${dbTable}.${this.quoteIdentifier(a.name)} is '${a.remark}';`;
|
commentSql += `comment on column ${dbTable}.${this.quoteIdentifier(a.name)} is '${QuoteEscape(a.remark)}';`;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
if (changeData.upd.length > 0) {
|
||||||
changeData.upd.forEach((a) => {
|
changeData.upd.forEach((a) => {
|
||||||
let cmtSql = `comment on column ${dbTable}.${this.quoteIdentifier(a.name)} is '${a.remark}';`;
|
let cmtSql = `comment on column ${dbTable}.${this.quoteIdentifier(a.name)} is '${QuoteEscape(a.remark)}';`;
|
||||||
if (a.remark && a.oldName === a.name) {
|
if (a.remark && a.oldName === a.name) {
|
||||||
commentSql += cmtSql;
|
commentSql += cmtSql;
|
||||||
}
|
}
|
||||||
@@ -412,7 +413,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
||||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
||||||
if (a.indexComment) {
|
if (a.indexComment) {
|
||||||
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${a.indexComment}'`);
|
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${QuoteEscape(a.indexComment)}'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -428,7 +429,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
let sql = '';
|
let sql = '';
|
||||||
if (tableData.tableComment != tableData.oldTableComment) {
|
if (tableData.tableComment != tableData.oldTableComment) {
|
||||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
||||||
sql = `COMMENT ON TABLE ${dbTable} is '${tableData.tableComment}';`;
|
sql = `COMMENT ON TABLE ${dbTable} is '${QuoteEscape(tableData.tableComment)}';`;
|
||||||
}
|
}
|
||||||
if (tableData.tableName != tableData.oldTableName) {
|
if (tableData.tableName != tableData.oldTableName) {
|
||||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.oldTableName)}`;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import (
|
|||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
@@ -24,6 +25,7 @@ import (
|
|||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"mayfly-go/pkg/utils/stringx"
|
"mayfly-go/pkg/utils/stringx"
|
||||||
"mayfly-go/pkg/ws"
|
"mayfly-go/pkg/ws"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -336,47 +338,93 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, table := range tables {
|
// 查询列信息,后面生成建表ddl和insert都需要列信息
|
||||||
writer.TryFlush()
|
columns, err := dbMeta.GetColumns(tables...)
|
||||||
quotedTable := dbMeta.QuoteIdentifier(table)
|
|
||||||
if needStruct {
|
|
||||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
|
|
||||||
writer.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS %s;\n", quotedTable))
|
|
||||||
ddl, err := dbMeta.GetTableDDL(table)
|
|
||||||
biz.ErrIsNil(err)
|
|
||||||
writer.WriteString(ddl + "\n")
|
|
||||||
}
|
|
||||||
if !needData {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", table))
|
|
||||||
|
|
||||||
// 达梦不支持begin语句
|
// 以表名分组,存放每个表的列信息
|
||||||
if dbConn.Info.Type != dbi.DbTypeDM {
|
columnMap := make(map[string][]dbi.Column)
|
||||||
writer.WriteString("BEGIN;\n")
|
for _, column := range columns {
|
||||||
|
columnMap[column.TableName] = append(columnMap[column.TableName], column)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按表名排序
|
||||||
|
sort.Strings(tables)
|
||||||
|
|
||||||
|
quoteSchema := dbMeta.QuoteIdentifier(dbConn.Info.CurrentSchema())
|
||||||
|
|
||||||
|
// 遍历获取每个表的信息
|
||||||
|
for _, tableName := range tables {
|
||||||
|
quoteTableName := dbMeta.QuoteIdentifier(tableName)
|
||||||
|
|
||||||
|
writer.TryFlush()
|
||||||
|
// 查询表信息,主要是为了查询表注释
|
||||||
|
tbs, err := dbMeta.GetTables(tableName)
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
|
panic(errorx.NewBiz(fmt.Sprintf("获取表信息失败:%s", tableName)))
|
||||||
}
|
}
|
||||||
insertSql := "INSERT INTO %s VALUES (%s);\n"
|
tabInfo := dbi.Table{
|
||||||
dbConn.WalkTableRows(ctx, table, func(record map[string]any, columns []*dbi.QueryColumn) error {
|
TableName: tableName,
|
||||||
var values []string
|
TableComment: tbs[0].TableComment,
|
||||||
writer.TryFlush()
|
}
|
||||||
for _, column := range columns {
|
|
||||||
value := record[column.Name]
|
// 生成表结构信息
|
||||||
if value == nil {
|
if needStruct {
|
||||||
values = append(values, "NULL")
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", tableName))
|
||||||
continue
|
tbDdlArr := dbMeta.GenerateTableDDL(columnMap[tableName], tabInfo, true)
|
||||||
}
|
for _, ddl := range tbDdlArr {
|
||||||
strValue, ok := value.(string)
|
writer.WriteString(ddl + ";\n")
|
||||||
if ok {
|
|
||||||
strValue = dbMeta.QuoteLiteral(strValue)
|
|
||||||
values = append(values, strValue)
|
|
||||||
} else {
|
|
||||||
values = append(values, anyx.ToString(value))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
writer.WriteString(fmt.Sprintf(insertSql, quotedTable, strings.Join(values, ", ")))
|
}
|
||||||
return nil
|
|
||||||
})
|
// 生成insert sql,数据在索引前,加速insert
|
||||||
writer.WriteString("COMMIT;\n")
|
if needData {
|
||||||
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表记录: %s \n-- ----------------------------\n", tableName))
|
||||||
|
|
||||||
|
dbMeta.BeforeDumpInsert(writer, quoteTableName)
|
||||||
|
|
||||||
|
// 获取列信息
|
||||||
|
quoteColNames := make([]string, 0)
|
||||||
|
for _, col := range columnMap[tableName] {
|
||||||
|
quoteColNames = append(quoteColNames, dbMeta.QuoteIdentifier(col.ColumnName))
|
||||||
|
}
|
||||||
|
|
||||||
|
converter := dbMeta.GetDataConverter()
|
||||||
|
_ = dbConn.WalkTableRows(context.Background(), quoteTableName, func(row map[string]any, _ []*dbi.QueryColumn) error {
|
||||||
|
rowValues := make([]string, len(columnMap[tableName]))
|
||||||
|
for i, col := range columnMap[tableName] {
|
||||||
|
rowValues[i] = converter.WrapValue(row[col.ColumnName], converter.GetDataType(string(col.DataType)))
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeInsert := dbMeta.BeforeDumpInsertSql(quoteSchema, quoteTableName)
|
||||||
|
insertSQL := fmt.Sprintf("%s INSERT INTO %s (%s) values(%s)", beforeInsert, quoteTableName, strings.Join(quoteColNames, ", "), strings.Join(rowValues, ", "))
|
||||||
|
writer.WriteString(insertSQL + ";\n")
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
dbMeta.AfterDumpInsert(writer, tableName, columnMap[tableName])
|
||||||
|
}
|
||||||
|
|
||||||
|
indexs, err := dbMeta.GetTableIndex(tableName)
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
|
// 过滤主键索引
|
||||||
|
idxs := make([]dbi.Index, 0)
|
||||||
|
for _, idx := range indexs {
|
||||||
|
if !idx.IsPrimaryKey {
|
||||||
|
idxs = append(idxs, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(idxs) > 0 {
|
||||||
|
// 最后添加索引
|
||||||
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表索引: %s \n-- ----------------------------\n", tableName))
|
||||||
|
sqlArr := dbMeta.GenerateIndexDDL(idxs, tabInfo)
|
||||||
|
for _, sqlStr := range sqlArr {
|
||||||
|
writer.WriteString(sqlStr + ";\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +498,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
|||||||
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
||||||
tn := rc.Query("tableName")
|
tn := rc.Query("tableName")
|
||||||
biz.NotEmpty(tn, "tableName不能为空")
|
biz.NotEmpty(tn, "tableName不能为空")
|
||||||
res, err := d.getDbConn(rc).GetMetaData().GetTableDDL(tn)
|
res, err := d.getDbConn(rc).GetMetaData().GetTableDDL(tn, false)
|
||||||
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbTransferTask struct {
|
type DbTransferTask struct {
|
||||||
@@ -53,13 +54,14 @@ func (d *DbTransferTask) DeleteTask(rc *req.Ctx) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *DbTransferTask) Run(rc *req.Ctx) {
|
func (d *DbTransferTask) Run(rc *req.Ctx) {
|
||||||
|
start := time.Now()
|
||||||
taskId := d.changeState(rc, entity.DbTransferTaskRunStateRunning)
|
taskId := d.changeState(rc, entity.DbTransferTaskRunStateRunning)
|
||||||
go d.DbTransferTask.Run(taskId, func(msg string, err error) {
|
go d.DbTransferTask.Run(taskId, func(msg string, err error) {
|
||||||
// 修改状态为停止
|
// 修改状态为停止
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Error(msg, err)
|
logx.Error(msg, err)
|
||||||
} else {
|
} else {
|
||||||
logx.Info(fmt.Sprintf("执行迁移完成,%s", msg))
|
logx.Info(fmt.Sprintf("执行迁移完成,%s, 耗时:%v", msg, time.Since(start)))
|
||||||
}
|
}
|
||||||
// 修改任务状态
|
// 修改任务状态
|
||||||
task := new(entity.DbTransferTask)
|
task := new(entity.DbTransferTask)
|
||||||
|
|||||||
@@ -26,6 +26,18 @@ func (g *gzipWriter) WriteString(data string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g *gzipWriter) Write(p []byte) (n int, err error) {
|
||||||
|
if g.aborted {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := g.writer.Write(p); err != nil {
|
||||||
|
g.aborted = true
|
||||||
|
biz.IsTrue(false, "数据库导出失败:%s", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func (g *gzipWriter) Close() {
|
func (g *gzipWriter) Close() {
|
||||||
g.writer.Close()
|
g.writer.Close()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -151,20 +151,21 @@ func (app *dataSyncAppImpl) RunCronJob(id uint64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
task.UpdFieldVal = strings.Trim(task.UpdFieldVal, " ")
|
task.UpdFieldVal = strings.Trim(task.UpdFieldVal, " ")
|
||||||
// 把UpdFieldVal尝试转为int,如果可以转为int,则不添加引号,否则添加引号
|
|
||||||
if _, err = strconv.Atoi(task.UpdFieldVal); err != nil {
|
|
||||||
updSql = fmt.Sprintf("and %s > '%s'", task.UpdField, task.UpdFieldVal)
|
|
||||||
} else {
|
|
||||||
updSql = fmt.Sprintf("and %s > %s", task.UpdField, task.UpdFieldVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果是oracle且数据类型是时间类型,则需要加上to_date函数
|
// 判断UpdFieldVal数据类型
|
||||||
if srcConn.Info.Type == dbi.DbTypeOracle {
|
var updFieldValType dbi.DataType
|
||||||
// 用正则判断数据类型是时间
|
if _, err = strconv.Atoi(task.UpdFieldVal); err != nil {
|
||||||
if dateTimeReg.MatchString(task.UpdFieldVal) {
|
if dateTimeReg.MatchString(task.UpdFieldVal) {
|
||||||
updSql = fmt.Sprintf("and %s > to_date('%s','yyyy-mm-dd hh24:mi:ss')", task.UpdField, task.UpdFieldVal)
|
updFieldValType = dbi.DataTypeDateTime
|
||||||
|
} else {
|
||||||
|
updFieldValType = dbi.DataTypeString
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
updFieldValType = dbi.DataTypeNumber
|
||||||
}
|
}
|
||||||
|
wrapUpdFieldVal := srcConn.GetMetaData().GetDataConverter().WrapValue(task.UpdFieldVal, updFieldValType)
|
||||||
|
updSql = fmt.Sprintf("and %s > %s", task.UpdField, wrapUpdFieldVal)
|
||||||
|
|
||||||
orderSql = "order by " + task.UpdField + " asc "
|
orderSql = "order by " + task.UpdField + " asc "
|
||||||
}
|
}
|
||||||
// 正则判断DataSql是否以where .*结尾,如果是则不添加where 1 = 1
|
// 正则判断DataSql是否以where .*结尾,如果是则不添加where 1 = 1
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import (
|
|||||||
"mayfly-go/pkg/gormx"
|
"mayfly-go/pkg/gormx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -153,15 +155,21 @@ func (app *dbTransferAppImpl) transferTables(task *entity.DbTransferTask, srcCon
|
|||||||
end("获取源表列信息失败", err)
|
end("获取源表列信息失败", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 以表名分组,存放每个表的列信息
|
// 以表名分组,存放每个表的列信息
|
||||||
columnMap := make(map[string][]dbi.Column)
|
columnMap := make(map[string][]dbi.Column)
|
||||||
for _, column := range columns {
|
for _, column := range columns {
|
||||||
columnMap[column.TableName] = append(columnMap[column.TableName], column)
|
columnMap[column.TableName] = append(columnMap[column.TableName], column)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 以表名排序
|
||||||
|
sortTableNames := collx.MapKeys(columnMap)
|
||||||
|
sort.Strings(sortTableNames)
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
for tbName, cols := range columnMap {
|
for _, tbName := range sortTableNames {
|
||||||
|
cols := columnMap[tbName]
|
||||||
targetCols := make([]dbi.Column, 0)
|
targetCols := make([]dbi.Column, 0)
|
||||||
for _, col := range cols {
|
for _, col := range cols {
|
||||||
colPtr := &col
|
colPtr := &col
|
||||||
@@ -183,7 +191,7 @@ func (app *dbTransferAppImpl) transferTables(task *entity.DbTransferTask, srcCon
|
|||||||
|
|
||||||
// 迁移数据
|
// 迁移数据
|
||||||
logx.Infof("开始迁移数据: 表名:%s", tbName)
|
logx.Infof("开始迁移数据: 表名:%s", tbName)
|
||||||
total, err := app.transferData(ctx, tbName, srcConn, srcDialect, targetConn, targetDialect)
|
total, err := app.transferData(ctx, tbName, targetCols, srcConn, srcDialect, targetConn, targetDialect)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
end(fmt.Sprintf("迁移数据失败: 表名:%s, error: %s", tbName, err.Error()), err)
|
end(fmt.Sprintf("迁移数据失败: 表名:%s, error: %s", tbName, err.Error()), err)
|
||||||
return
|
return
|
||||||
@@ -216,27 +224,16 @@ func (app *dbTransferAppImpl) transferTables(task *entity.DbTransferTask, srcCon
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string, srcConn *dbi.DbConn, srcDialect dbi.Dialect, targetConn *dbi.DbConn, targetDialect dbi.Dialect) (int, error) {
|
func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string, targetColumns []dbi.Column, srcConn *dbi.DbConn, srcDialect dbi.Dialect, targetConn *dbi.DbConn, targetDialect dbi.Dialect) (int, error) {
|
||||||
result := make([]map[string]any, 0)
|
result := make([]map[string]any, 0)
|
||||||
total := 0 // 总条数
|
total := 0 // 总条数
|
||||||
batchSize := 1000 // 每次查询并迁移1000条数据
|
batchSize := 1000 // 每次查询并迁移1000条数据
|
||||||
var queryColumns []*dbi.QueryColumn
|
|
||||||
var err error
|
var err error
|
||||||
srcMeta := srcConn.GetMetaData()
|
srcMeta := srcConn.GetMetaData()
|
||||||
srcConverter := srcMeta.GetDataConverter()
|
srcConverter := srcMeta.GetDataConverter()
|
||||||
|
|
||||||
// 游标查询源表数据,并批量插入目标表
|
// 游标查询源表数据,并批量插入目标表
|
||||||
err = srcConn.WalkTableRows(ctx, tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
err = srcConn.WalkTableRows(ctx, tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
|
||||||
if len(queryColumns) == 0 {
|
|
||||||
|
|
||||||
for _, col := range columns {
|
|
||||||
queryColumns = append(queryColumns, &dbi.QueryColumn{
|
|
||||||
Name: targetConn.GetMetaData().QuoteIdentifier(col.Name),
|
|
||||||
Type: col.Type,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
total++
|
total++
|
||||||
rawValue := map[string]any{}
|
rawValue := map[string]any{}
|
||||||
for _, column := range columns {
|
for _, column := range columns {
|
||||||
@@ -246,7 +243,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string
|
|||||||
}
|
}
|
||||||
result = append(result, rawValue)
|
result = append(result, rawValue)
|
||||||
if total%batchSize == 0 {
|
if total%batchSize == 0 {
|
||||||
err = app.transfer2Target(targetConn, queryColumns, result, targetDialect, tableName)
|
err = app.transfer2Target(targetConn, targetColumns, result, targetDialect, tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Error("批量插入目标表数据失败", err)
|
logx.Error("批量插入目标表数据失败", err)
|
||||||
return err
|
return err
|
||||||
@@ -257,7 +254,7 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string
|
|||||||
})
|
})
|
||||||
// 处理剩余的数据
|
// 处理剩余的数据
|
||||||
if len(result) > 0 {
|
if len(result) > 0 {
|
||||||
err = app.transfer2Target(targetConn, queryColumns, result, targetDialect, tableName)
|
err = app.transfer2Target(targetConn, targetColumns, result, targetDialect, tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Error(fmt.Sprintf("批量插入目标表数据失败,表名:%s", tableName), err)
|
logx.Error(fmt.Sprintf("批量插入目标表数据失败,表名:%s", tableName), err)
|
||||||
return 0, err
|
return 0, err
|
||||||
@@ -266,23 +263,36 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string
|
|||||||
return total, err
|
return total, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (app *dbTransferAppImpl) transfer2Target(targetConn *dbi.DbConn, cols []*dbi.QueryColumn, result []map[string]any, targetDialect dbi.Dialect, tbName string) error {
|
func (app *dbTransferAppImpl) transfer2Target(targetConn *dbi.DbConn, targetColumns []dbi.Column, result []map[string]any, targetDialect dbi.Dialect, tbName string) error {
|
||||||
tx, err := targetConn.Begin()
|
tx, err := targetConn.Begin()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
targetMeta := targetConn.GetMetaData()
|
||||||
|
|
||||||
// 收集字段名
|
// 收集字段名
|
||||||
var columnNames []string
|
var columnNames []string
|
||||||
for _, col := range cols {
|
for _, col := range targetColumns {
|
||||||
columnNames = append(columnNames, col.Name)
|
columnNames = append(columnNames, targetMeta.QuoteIdentifier(col.ColumnName))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 从目标库数据中取出源库字段对应的值
|
// 从目标库数据中取出源库字段对应的值
|
||||||
values := make([][]any, 0)
|
values := make([][]any, 0)
|
||||||
for _, record := range result {
|
for _, record := range result {
|
||||||
rawValue := make([]any, 0)
|
rawValue := make([]any, 0)
|
||||||
for _, cn := range columnNames {
|
for _, tc := range targetColumns {
|
||||||
rawValue = append(rawValue, record[targetConn.GetMetaData().RemoveQuote(cn)])
|
columnName := tc.ColumnName
|
||||||
|
val := record[targetMeta.RemoveQuote(columnName)]
|
||||||
|
if !tc.Nullable {
|
||||||
|
// 如果val是文本,则设置为空格字符
|
||||||
|
switch val.(type) {
|
||||||
|
case string:
|
||||||
|
if val == "" {
|
||||||
|
val = " "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawValue = append(rawValue, val)
|
||||||
}
|
}
|
||||||
values = append(values, rawValue)
|
values = append(values, rawValue)
|
||||||
}
|
}
|
||||||
@@ -312,6 +322,18 @@ func (app *dbTransferAppImpl) transferIndex(_ context.Context, tableInfo dbi.Tab
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 过滤主键索引
|
||||||
|
idxs := make([]dbi.Index, 0)
|
||||||
|
for _, idx := range indexs {
|
||||||
|
if !idx.IsPrimaryKey {
|
||||||
|
idxs = append(idxs, idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(idxs) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// 通过表名、索引信息生成建索引语句,并执行到目标表
|
// 通过表名、索引信息生成建索引语句,并执行到目标表
|
||||||
return targetDialect.CreateIndex(tableInfo, indexs)
|
return targetDialect.CreateIndex(tableInfo, idxs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ type MetaData interface {
|
|||||||
// 获取指定表名的所有列元信息
|
// 获取指定表名的所有列元信息
|
||||||
GetColumns(tableNames ...string) ([]Column, error)
|
GetColumns(tableNames ...string) ([]Column, error)
|
||||||
|
|
||||||
|
// 根据数据库类型修复字段长度、精度等
|
||||||
|
FixColumn(column *Column)
|
||||||
|
|
||||||
// 获取表主键字段名,没有主键标识则默认第一个字段
|
// 获取表主键字段名,没有主键标识则默认第一个字段
|
||||||
GetPrimaryKey(tableName string) (string, error)
|
GetPrimaryKey(tableName string) (string, error)
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ type MetaData interface {
|
|||||||
GetTableIndex(tableName string) ([]Index, error)
|
GetTableIndex(tableName string) ([]Index, error)
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
GetTableDDL(tableName string) (string, error)
|
GetTableDDL(tableName string, dropBeforeCreate bool) (string, error)
|
||||||
|
|
||||||
GenerateTableDDL(columns []Column, tableInfo Table, dropBeforeCreate bool) []string
|
GenerateTableDDL(columns []Column, tableInfo Table, dropBeforeCreate bool) []string
|
||||||
|
|
||||||
@@ -44,6 +47,9 @@ type MetaData interface {
|
|||||||
GetDataConverter() DataConverter
|
GetDataConverter() DataConverter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GenerateSQLStepFunc 生成insert sql的step函数,用于生成insert sql时,每生成100条sql时调用
|
||||||
|
type GenerateSQLStepFunc func(sqlArr []string)
|
||||||
|
|
||||||
// 数据库服务实例信息
|
// 数据库服务实例信息
|
||||||
type DbServer struct {
|
type DbServer struct {
|
||||||
Version string `json:"version"` // 版本信息
|
Version string `json:"version"` // 版本信息
|
||||||
@@ -100,6 +106,7 @@ type Index struct {
|
|||||||
IndexComment string `json:"indexComment"` // 备注
|
IndexComment string `json:"indexComment"` // 备注
|
||||||
SeqInIndex int `json:"seqInIndex"`
|
SeqInIndex int `json:"seqInIndex"`
|
||||||
IsUnique bool `json:"isUnique"`
|
IsUnique bool `json:"isUnique"`
|
||||||
|
IsPrimaryKey bool `json:"isPrimaryKey"` // 是否是主键索引,某些情况需要判断并过滤掉主键索引
|
||||||
}
|
}
|
||||||
|
|
||||||
type ColumnDataType string
|
type ColumnDataType string
|
||||||
@@ -152,6 +159,13 @@ type DataConverter interface {
|
|||||||
|
|
||||||
// 根据数据类型解析数据为符合要求的指定类型等
|
// 根据数据类型解析数据为符合要求的指定类型等
|
||||||
ParseData(dbColumnValue any, dataType DataType) any
|
ParseData(dbColumnValue any, dataType DataType) any
|
||||||
|
|
||||||
|
// WrapValue 根据数据类型包装value,如:
|
||||||
|
// 1.数字型:不需要引号,
|
||||||
|
// 2.文本型:需要用引号包裹,单引号需要转义,换行符转义,
|
||||||
|
// 3.date型:需要格式化成对应的字符串,如:time:hh:mm:ss.SSS date: yyyy-mm-dd datetime:
|
||||||
|
// 4.特殊:oracle date型需要用函数包裹:to_timestamp('%s', 'yyyy-mm-dd hh24:mi:ss')
|
||||||
|
WrapValue(dbColumnValue any, dataType DataType) string
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------- 元数据sql操作 -------------------------
|
// ------------------------- 元数据sql操作 -------------------------
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package dbi
|
|||||||
import (
|
import (
|
||||||
pq "gitee.com/liuzongyang/libpq"
|
pq "gitee.com/liuzongyang/libpq"
|
||||||
"github.com/kanzihuang/vitess/go/vt/sqlparser"
|
"github.com/kanzihuang/vitess/go/vt/sqlparser"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseMetaData interface {
|
type BaseMetaData interface {
|
||||||
@@ -13,6 +15,9 @@ type BaseMetaData interface {
|
|||||||
// 用于引用 SQL 标识符(关键字)的字符串
|
// 用于引用 SQL 标识符(关键字)的字符串
|
||||||
GetIdentifierQuoteString() string
|
GetIdentifierQuoteString() string
|
||||||
|
|
||||||
|
// 引号转义,多用于sql注释转义,防止拼接sql报错,如: comment xx is '注''释' 最终注释文本为: 注'释
|
||||||
|
QuoteEscape(str string) string
|
||||||
|
|
||||||
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
|
// QuoteLiteral quotes a 'literal' (e.g. a parameter, often used to pass literal
|
||||||
// to DDL and other statements that do not accept parameters) to be used as part
|
// to DDL and other statements that do not accept parameters) to be used as part
|
||||||
// of an SQL statement. For example:
|
// of an SQL statement. For example:
|
||||||
@@ -25,6 +30,12 @@ type BaseMetaData interface {
|
|||||||
QuoteLiteral(literal string) string
|
QuoteLiteral(literal string) string
|
||||||
|
|
||||||
SqlParserDialect() sqlparser.Dialect
|
SqlParserDialect() sqlparser.Dialect
|
||||||
|
|
||||||
|
BeforeDumpInsert(writer io.Writer, tableName string)
|
||||||
|
|
||||||
|
BeforeDumpInsertSql(quoteSchema string, quoteTableName string) string
|
||||||
|
|
||||||
|
AfterDumpInsert(writer io.Writer, tableName string, columns []Column)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 默认实现,若需要覆盖,则由各个数据库MetaData实现去覆盖重写
|
// 默认实现,若需要覆盖,则由各个数据库MetaData实现去覆盖重写
|
||||||
@@ -39,6 +50,10 @@ func (dd *DefaultMetaData) GetIdentifierQuoteString() string {
|
|||||||
return `"`
|
return `"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dd *DefaultMetaData) QuoteEscape(str string) string {
|
||||||
|
return strings.Replace(str, `'`, `''`, -1)
|
||||||
|
}
|
||||||
|
|
||||||
func (dd *DefaultMetaData) QuoteLiteral(literal string) string {
|
func (dd *DefaultMetaData) QuoteLiteral(literal string) string {
|
||||||
return pq.QuoteLiteral(literal)
|
return pq.QuoteLiteral(literal)
|
||||||
}
|
}
|
||||||
@@ -46,3 +61,13 @@ func (dd *DefaultMetaData) QuoteLiteral(literal string) string {
|
|||||||
func (dd *DefaultMetaData) SqlParserDialect() sqlparser.Dialect {
|
func (dd *DefaultMetaData) SqlParserDialect() sqlparser.Dialect {
|
||||||
return sqlparser.PostgresDialect{}
|
return sqlparser.PostgresDialect{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dd *DefaultMetaData) BeforeDumpInsert(writer io.Writer, tableName string) {
|
||||||
|
writer.Write([]byte("BEGIN;\n"))
|
||||||
|
}
|
||||||
|
func (dd *DefaultMetaData) BeforeDumpInsertSql(quoteSchema string, tableName string) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
func (dd *DefaultMetaData) AfterDumpInsert(writer io.Writer, tableName string, columns []Column) {
|
||||||
|
writer.Write([]byte("COMMIT;\n"))
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,7 +52,8 @@ WHERE a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
|||||||
order by a.TABLE_NAME, a.index_name, c.column_position asc
|
order by a.TABLE_NAME, a.index_name, c.column_position asc
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
--DM_COLUMN_MA 表列信息
|
--DM_COLUMN_MA 表列信息
|
||||||
select a.table_name as TABLE_NAME,
|
select a.owner,
|
||||||
|
a.table_name as TABLE_NAME,
|
||||||
a.column_name as COLUMN_NAME,
|
a.column_name as COLUMN_NAME,
|
||||||
case when a.NULLABLE = 'Y' then 'YES' when a.NULLABLE = 'N' then 'NO' else 'NO' end as NULLABLE,
|
case when a.NULLABLE = 'Y' then 'YES' when a.NULLABLE = 'N' then 'NO' else 'NO' end as NULLABLE,
|
||||||
a.data_type as DATA_TYPE,
|
a.data_type as DATA_TYPE,
|
||||||
@@ -61,25 +62,21 @@ select a.table_name
|
|||||||
a.data_scale as NUM_SCALE,
|
a.data_scale as NUM_SCALE,
|
||||||
b.comments as COLUMN_COMMENT,
|
b.comments as COLUMN_COMMENT,
|
||||||
a.data_default as COLUMN_DEFAULT,
|
a.data_default as COLUMN_DEFAULT,
|
||||||
case when t.COL_NAME = a.column_name then 1 else 0 end as IS_IDENTITY,
|
case when t.INFO2 & 0x01 = 0x01 then 1 else 0 end as IS_IDENTITY,
|
||||||
case when t2.constraint_type = 'P' then 1 else 0 end as IS_PRIMARY_KEY
|
case when t2.constraint_type = 'P' then 1 else 0 end as IS_PRIMARY_KEY
|
||||||
from all_tab_columns a
|
from all_tab_columns a
|
||||||
left join user_col_comments b
|
left join user_col_comments b
|
||||||
on b.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
on b.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||||
and b.table_name = a.table_name
|
and b.table_name = a.table_name
|
||||||
and a.column_name = b.column_name
|
and a.column_name = b.column_name
|
||||||
left join (select b.owner, b.TABLE_NAME, a.NAME as COL_NAME
|
left join (select c1.*, c2.object_name, c2.owner
|
||||||
from SYS.SYSCOLUMNS a,
|
FROM SYS.SYSCOLUMNS c1
|
||||||
SYS.all_tables b,
|
join SYS.all_objects c2 on c1.id = c2.object_id and c2.object_type = 'TABLE') t
|
||||||
SYS.SYSOBJECTS c
|
on t.object_name = a.table_name and t.owner = a.owner and t.NAME = a.column_name
|
||||||
where a.INFO2 & 0x01 = 0x01
|
|
||||||
and a.ID = c.ID
|
|
||||||
and c.NAME = b.TABLE_NAME) t
|
|
||||||
on t.table_name = a.table_name and t.owner = a.owner
|
|
||||||
left join (select uc.OWNER, uic.column_name, uic.table_name, uc.constraint_type
|
left join (select uc.OWNER, uic.column_name, uic.table_name, uc.constraint_type
|
||||||
from user_ind_columns uic
|
from user_ind_columns uic
|
||||||
left join user_constraints uc on uic.index_name = uc.index_name) t2
|
left join user_constraints uc on uic.index_name = uc.index_name) t2
|
||||||
on t2.table_name = t.table_name and a.column_name = t2.column_name
|
on t2.table_name = t.object_name and a.column_name = t2.column_name and t2.OWNER = a.owner
|
||||||
where a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
where a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||||
and a.table_name in (%s)
|
and a.table_name in (%s)
|
||||||
order by a.table_name,
|
order by a.table_name,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ where ss.name = ?
|
|||||||
{{if .tableNames}}
|
{{if .tableNames}}
|
||||||
and t.name in ({{.tableNames}})
|
and t.name in ({{.tableNames}})
|
||||||
{{end}}
|
{{end}}
|
||||||
ORDER BY t.name DESC;
|
ORDER BY t.name ASC;
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
--MSSQL_INDEX_INFO 索引信息
|
--MSSQL_INDEX_INFO 索引信息
|
||||||
SELECT ind.name AS indexName,
|
SELECT ind.name AS indexName,
|
||||||
@@ -46,7 +46,11 @@ SELECT ind.name AS indexName,
|
|||||||
END AS indexType,
|
END AS indexType,
|
||||||
IIF(ind.is_unique = 'true', 1, 0) AS isUnique,
|
IIF(ind.is_unique = 'true', 1, 0) AS isUnique,
|
||||||
ic.key_ordinal AS seqInIndex,
|
ic.key_ordinal AS seqInIndex,
|
||||||
idx.value AS indexComment
|
idx.value AS indexComment,
|
||||||
|
CASE
|
||||||
|
WHEN LEFT(ind.name, 3) = 'PK_' THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS isPrimaryKey
|
||||||
FROM sys.indexes ind
|
FROM sys.indexes ind
|
||||||
LEFT JOIN sys.tables t on t.object_id = ind.object_id
|
LEFT JOIN sys.tables t on t.object_id = ind.object_id
|
||||||
LEFT JOIN sys.schemas ss on t.schema_id = ss.schema_id
|
LEFT JOIN sys.schemas ss on t.schema_id = ss.schema_id
|
||||||
|
|||||||
@@ -35,7 +35,8 @@ SELECT
|
|||||||
index_type indexType,
|
index_type indexType,
|
||||||
IF(non_unique, 0, 1) isUnique,
|
IF(non_unique, 0, 1) isUnique,
|
||||||
SEQ_IN_INDEX seqInIndex,
|
SEQ_IN_INDEX seqInIndex,
|
||||||
INDEX_COMMENT indexComment
|
INDEX_COMMENT indexComment,
|
||||||
|
index_name = 'PRIMARY' as isPrimaryKey
|
||||||
FROM
|
FROM
|
||||||
information_schema.STATISTICS
|
information_schema.STATISTICS
|
||||||
WHERE
|
WHERE
|
||||||
|
|||||||
@@ -38,7 +38,12 @@ SELECT ai.INDEX_NAME AS INDEX_NAME,
|
|||||||
WHERE aic.INDEX_OWNER = ai.OWNER
|
WHERE aic.INDEX_OWNER = ai.OWNER
|
||||||
AND aic.INDEX_NAME = ai.INDEX_NAME
|
AND aic.INDEX_NAME = ai.INDEX_NAME
|
||||||
AND aic.TABLE_NAME = ai.TABLE_NAME
|
AND aic.TABLE_NAME = ai.TABLE_NAME
|
||||||
AND ROWNUM = 1) AS INDEX_COMMENT
|
AND ROWNUM = 1) AS INDEX_COMMENT,
|
||||||
|
CASE
|
||||||
|
WHEN ai.INDEX_NAME like 'PK_%%' THEN 1
|
||||||
|
WHEN ai.INDEX_NAME like 'SYS_%%' THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END AS IS_PRIMARY
|
||||||
FROM ALL_INDEXES ai
|
FROM ALL_INDEXES ai
|
||||||
WHERE ai.OWNER = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM DUAL)
|
WHERE ai.OWNER = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM DUAL)
|
||||||
AND ai.table_name = '%s'
|
AND ai.table_name = '%s'
|
||||||
@@ -50,14 +55,14 @@ SELECT a.TABLE_NAME as TABLE_NAME,
|
|||||||
when a.NULLABLE = 'Y' then 'YES'
|
when a.NULLABLE = 'Y' then 'YES'
|
||||||
when a.NULLABLE = 'N' then 'NO'
|
when a.NULLABLE = 'N' then 'NO'
|
||||||
else 'NO' end as NULLABLE,
|
else 'NO' end as NULLABLE,
|
||||||
case
|
a.DATA_TYPE as DATA_TYPE,
|
||||||
when a.DATA_PRECISION > 0 then a.DATA_TYPE
|
a.DATA_LENGTH as CHAR_MAX_LENGTH,
|
||||||
else (a.DATA_TYPE || '(' || a.DATA_LENGTH || ')') end as COLUMN_TYPE,
|
a.DATA_PRECISION as NUM_PRECISION,
|
||||||
|
a.DATA_SCALE as NUM_SCALE,
|
||||||
b.COMMENTS as COLUMN_COMMENT,
|
b.COMMENTS as COLUMN_COMMENT,
|
||||||
a.DATA_DEFAULT as COLUMN_DEFAULT,
|
a.DATA_DEFAULT as COLUMN_DEFAULT,
|
||||||
a.DATA_SCALE as NUM_SCALE,
|
CASE WHEN d.pri IS NOT NULL THEN 1 ELSE 0 END as IS_PRIMARY_KEY,
|
||||||
CASE WHEN d.pri IS NOT NULL THEN 1 ELSE 0 END as IS_PRIMARY_KEY,
|
CASE WHEN a.IDENTITY_COLUMN = 'YES' THEN 1 ELSE 0 END as IS_IDENTITY
|
||||||
CASE WHEN a.IDENTITY_COLUMN = 'YES' THEN 1 ELSE 0 END as IS_IDENTITY
|
|
||||||
FROM ALL_TAB_COLUMNS a
|
FROM ALL_TAB_COLUMNS a
|
||||||
LEFT JOIN ALL_COL_COMMENTS b
|
LEFT JOIN ALL_COL_COMMENTS b
|
||||||
on a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME
|
on a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME
|
||||||
|
|||||||
@@ -35,29 +35,28 @@ where
|
|||||||
order by c.relname
|
order by c.relname
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
--PGSQL_INDEX_INFO 表索引信息
|
--PGSQL_INDEX_INFO 表索引信息
|
||||||
SELECT
|
SELECT indexname AS "indexName",
|
||||||
indexname AS "indexName",
|
'BTREE' AS "IndexType",
|
||||||
'BTREE' AS "IndexType",
|
case when indexdef like 'CREATE UNIQUE INDEX%%' then 1 else 0 end as "isUnique",
|
||||||
case when indexdef like 'CREATE UNIQUE INDEX%%' then 1 else 0 end as "isUnique",
|
obj_description(b.oid, 'pg_class') AS "indexComment",
|
||||||
obj_description(b.oid, 'pg_class') AS "indexComment",
|
indexdef AS "indexDef",
|
||||||
indexdef AS "indexDef",
|
c.attname AS "columnName",
|
||||||
c.attname AS "columnName",
|
c.attnum AS "seqInIndex",
|
||||||
c.attnum AS "seqInIndex"
|
case when indexname like '%_pkey' then 1 else 0 end as "isPrimaryKey"
|
||||||
FROM pg_indexes a
|
FROM pg_indexes a
|
||||||
join pg_class b on a.indexname = b.relname
|
join pg_class b on a.indexname = b.relname
|
||||||
join pg_attribute c on b.oid = c.attrelid
|
join pg_attribute c on b.oid = c.attrelid
|
||||||
WHERE a.schemaname = (select current_schema())
|
WHERE a.schemaname = (select current_schema())
|
||||||
AND a.tablename = '%s';
|
AND a.tablename = '%s';
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
--PGSQL_COLUMN_MA 表列信息
|
--PGSQL_COLUMN_MA 表列信息
|
||||||
SELECT a.*,
|
SELECT a.table_name AS "tableName",
|
||||||
a.table_name AS "tableName",
|
|
||||||
a.column_name AS "columnName",
|
a.column_name AS "columnName",
|
||||||
a.is_nullable AS "nullable",
|
a.is_nullable AS "nullable",
|
||||||
a.udt_name AS "dataType",
|
a.udt_name AS "dataType",
|
||||||
a.character_maximum_length AS "charMaxLength",
|
a.character_maximum_length AS "charMaxLength",
|
||||||
a.numeric_precision AS "numPrecision",
|
a.numeric_precision AS "numPrecision",
|
||||||
a.column_default AS "columnDefault",
|
case when a.column_default like 'nextval%%' then null else a.column_default end AS "columnDefault",
|
||||||
a.numeric_scale AS "numScale",
|
a.numeric_scale AS "numScale",
|
||||||
case when a.column_default like 'nextval%%' then 1 else 0 end AS "isIdentity",
|
case when a.column_default like 'nextval%%' then 1 else 0 end AS "isIdentity",
|
||||||
case when b.column_name is not null then 1 else 0 end AS "isPrimaryKey",
|
case when b.column_name is not null then 1 else 0 end AS "isPrimaryKey",
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ func (dd *DMDialect) batchInsertMerge(tx *sql.Tx, tableName string, columns []st
|
|||||||
func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||||
tableName := copy.TableName
|
tableName := copy.TableName
|
||||||
metadata := dd.dc.GetMetaData()
|
metadata := dd.dc.GetMetaData()
|
||||||
ddl, err := metadata.GetTableDDL(tableName)
|
ddl, err := metadata.GetTableDDL(tableName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -177,21 +177,14 @@ func (dd *DMDialect) ToCommonColumn(dialectColumn *dbi.Column) {
|
|||||||
|
|
||||||
func (dd *DMDialect) ToColumn(commonColumn *dbi.Column) {
|
func (dd *DMDialect) ToColumn(commonColumn *dbi.Column) {
|
||||||
ctype := dmColumnTypeMap[commonColumn.DataType]
|
ctype := dmColumnTypeMap[commonColumn.DataType]
|
||||||
|
meta := dd.dc.GetMetaData()
|
||||||
|
|
||||||
if ctype == "" {
|
if ctype == "" {
|
||||||
commonColumn.DataType = "VARCHAR"
|
commonColumn.DataType = "VARCHAR"
|
||||||
commonColumn.CharMaxLength = 2000
|
commonColumn.CharMaxLength = 2000
|
||||||
} else {
|
} else {
|
||||||
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
||||||
// 如果是date,不设长度
|
meta.FixColumn(commonColumn)
|
||||||
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(ctype)) {
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
commonColumn.NumPrecision = 0
|
|
||||||
} else
|
|
||||||
// 如果是char且长度未设置,则默认长度2000
|
|
||||||
if collx.ArrayAnyMatches([]string{"char"}, strings.ToLower(ctype)) && commonColumn.CharMaxLength == 0 {
|
|
||||||
commonColumn.CharMaxLength = 2000
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,7 +205,15 @@ func (dd *DMDialect) CreateTable(columns []dbi.Column, tableInfo dbi.Table, drop
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DMDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
|
func (dd *DMDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
|
||||||
sqls := dd.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
|
sqlArr := dd.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
|
||||||
_, err := dd.dc.Exec(strings.Join(sqls, ";"))
|
// 达梦需要分开执行sql
|
||||||
return err
|
if len(sqlArr) > 0 {
|
||||||
|
for _, sqlStr := range sqlArr {
|
||||||
|
_, err := dd.dc.Exec(sqlStr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dm
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
@@ -112,11 +113,24 @@ func (dd *DMMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
|||||||
NumPrecision: cast.ToInt(re["NUM_PRECISION"]),
|
NumPrecision: cast.ToInt(re["NUM_PRECISION"]),
|
||||||
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
||||||
}
|
}
|
||||||
|
dd.FixColumn(&column)
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dd *DMMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
// 如果是date,不设长度
|
||||||
|
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(string(column.DataType))) {
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
column.NumPrecision = 0
|
||||||
|
} else
|
||||||
|
// 如果是char且长度未设置,则默认长度2000
|
||||||
|
if collx.ArrayAnyMatches([]string{"char"}, strings.ToLower(string(column.DataType))) && column.CharMaxLength == 0 {
|
||||||
|
column.CharMaxLength = 2000
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (dd *DMMetaData) GetPrimaryKey(tablename string) (string, error) {
|
func (dd *DMMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := dd.GetColumns(tablename)
|
columns, err := dd.GetColumns(tablename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -150,6 +164,7 @@ func (dd *DMMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
IndexComment: cast.ToString(re["INDEX_COMMENT"]),
|
IndexComment: cast.ToString(re["INDEX_COMMENT"]),
|
||||||
IsUnique: cast.ToInt(re["IS_UNIQUE"]) == 1,
|
IsUnique: cast.ToInt(re["IS_UNIQUE"]) == 1,
|
||||||
SeqInIndex: cast.ToInt(re["SEQ_IN_INDEX"]),
|
SeqInIndex: cast.ToInt(re["SEQ_IN_INDEX"]),
|
||||||
|
IsPrimaryKey: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||||
@@ -206,7 +221,7 @@ func (dd *DMMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.GetColumnType(), incr, nullAble, defVal)
|
columnSql := fmt.Sprintf(" %s %s%s%s%s", colName, column.GetColumnType(), incr, nullAble, defVal)
|
||||||
return columnSql
|
return columnSql
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -215,27 +230,25 @@ func (dd *DMMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table)
|
|||||||
meta := dd.dc.GetMetaData()
|
meta := dd.dc.GetMetaData()
|
||||||
sqls := make([]string, 0)
|
sqls := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
|
|
||||||
|
|
||||||
sqls = append(sqls, fmt.Sprintf("create %s index %s on %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
|
// 取出列名,添加引号
|
||||||
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
|
colNames := make([]string, len(cols))
|
||||||
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sqls = append(sqls, fmt.Sprintf("create %s index %s on %s(%s)", unique, index.IndexName, meta.QuoteIdentifier(tableInfo.TableName), strings.Join(colNames, ",")))
|
||||||
}
|
}
|
||||||
return sqls
|
return sqls
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DMMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
func (dd *DMMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
||||||
meta := dd.dc.GetMetaData()
|
meta := dd.dc.GetMetaData()
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
|
||||||
tbName := meta.QuoteIdentifier(tableInfo.TableName)
|
tbName := meta.QuoteIdentifier(tableInfo.TableName)
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
|
|
||||||
@@ -253,25 +266,21 @@ func (dd *DMMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table
|
|||||||
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
|
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
|
||||||
}
|
}
|
||||||
fields = append(fields, dd.genColumnBasicSql(column))
|
fields = append(fields, dd.genColumnBasicSql(column))
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
|
||||||
if column.ColumnComment != "" {
|
if column.ColumnComment != "" {
|
||||||
comment := replacer.Replace(column.ColumnComment)
|
comment := meta.QuoteEscape(column.ColumnComment)
|
||||||
columnComments = append(columnComments, fmt.Sprintf("comment on column %s.%s is '%s'", tbName, meta.QuoteIdentifier(column.ColumnName), comment))
|
columnComments = append(columnComments, fmt.Sprintf("comment on column %s.%s is '%s'", tbName, meta.QuoteIdentifier(column.ColumnName), comment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createSql += strings.Join(fields, ",")
|
createSql += strings.Join(fields, ",\n")
|
||||||
if len(pks) > 0 {
|
if len(pks) > 0 {
|
||||||
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
|
createSql += fmt.Sprintf(",\n PRIMARY KEY (%s)", strings.Join(pks, ","))
|
||||||
}
|
}
|
||||||
createSql += ")"
|
createSql += "\n)"
|
||||||
|
|
||||||
tableCommentSql := ""
|
tableCommentSql := ""
|
||||||
if tableInfo.TableComment != "" {
|
if tableInfo.TableComment != "" {
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
comment := meta.QuoteEscape(tableInfo.TableComment)
|
||||||
comment := replacer.Replace(tableInfo.TableComment)
|
tableCommentSql = fmt.Sprintf("comment on table %s is '%s'", tbName, comment)
|
||||||
if comment != "" {
|
|
||||||
tableCommentSql = fmt.Sprintf(" comment on table %s is '%s'", tbName, comment)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlArr = append(sqlArr, createSql)
|
sqlArr = append(sqlArr, createSql)
|
||||||
@@ -287,13 +296,12 @@ func (dd *DMMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (dd *DMMetaData) GetTableDDL(tableName string) (string, error) {
|
func (dd *DMMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
|
|
||||||
// 1.获取表信息
|
// 1.获取表信息
|
||||||
tbs, err := dd.GetTables(tableName)
|
tbs, err := dd.GetTables(tableName)
|
||||||
tableInfo := &dbi.Table{}
|
tableInfo := &dbi.Table{}
|
||||||
if err != nil && len(tbs) > 0 {
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
|
|
||||||
logx.Errorf("获取表信息失败, %s", tableName)
|
logx.Errorf("获取表信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -306,7 +314,7 @@ func (dd *DMMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
logx.Errorf("获取列信息失败, %s", tableName)
|
logx.Errorf("获取列信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tableDDLArr := dd.GenerateTableDDL(columns, *tableInfo, false)
|
tableDDLArr := dd.GenerateTableDDL(columns, *tableInfo, dropBeforeCreate)
|
||||||
// 3.获取索引信息
|
// 3.获取索引信息
|
||||||
indexs, err := dd.GetTableIndex(tableName)
|
indexs, err := dd.GetTableIndex(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -315,7 +323,7 @@ func (dd *DMMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
}
|
}
|
||||||
// 组装返回
|
// 组装返回
|
||||||
tableDDLArr = append(tableDDLArr, dd.GenerateIndexDDL(indexs, *tableInfo)...)
|
tableDDLArr = append(tableDDLArr, dd.GenerateIndexDDL(indexs, *tableInfo)...)
|
||||||
return strings.Join(tableDDLArr, ";"), nil
|
return strings.Join(tableDDLArr, ";\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取DM当前连接的库可访问的schemaNames
|
// 获取DM当前连接的库可访问的schemaNames
|
||||||
@@ -332,6 +340,18 @@ func (dd *DMMetaData) GetSchemas() ([]string, error) {
|
|||||||
return schemaNames, nil
|
return schemaNames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dd *DMMetaData) BeforeDumpInsert(writer io.Writer, tableName string) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dd *DMMetaData) BeforeDumpInsertSql(quoteSchema string, tableName string) string {
|
||||||
|
return fmt.Sprintf("set identity_insert %s on;", tableName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dd *DMMetaData) AfterDumpInsert(writer io.Writer, tableName string, columns []dbi.Column) {
|
||||||
|
writer.Write([]byte("COMMIT;\n"))
|
||||||
|
}
|
||||||
|
|
||||||
func (dd *DMMetaData) GetDataConverter() dbi.DataConverter {
|
func (dd *DMMetaData) GetDataConverter() dbi.DataConverter {
|
||||||
return converter
|
return converter
|
||||||
}
|
}
|
||||||
@@ -406,7 +426,7 @@ var (
|
|||||||
type DataConverter struct {
|
type DataConverter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DataConverter) GetDataType(dbColumnType string) dbi.DataType {
|
func (dc *DataConverter) GetDataType(dbColumnType string) dbi.DataType {
|
||||||
if numberRegexp.MatchString(dbColumnType) {
|
if numberRegexp.MatchString(dbColumnType) {
|
||||||
return dbi.DataTypeNumber
|
return dbi.DataTypeNumber
|
||||||
}
|
}
|
||||||
@@ -422,23 +442,38 @@ func (dd *DataConverter) GetDataType(dbColumnType string) dbi.DataType {
|
|||||||
return dbi.DataTypeString
|
return dbi.DataTypeString
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
str := anyx.ToString(dbColumnValue)
|
str := anyx.ToString(dbColumnValue)
|
||||||
switch dataType {
|
switch dataType {
|
||||||
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateTime)
|
return res.Format(time.DateTime)
|
||||||
case dbi.DataTypeDate: // "2024-01-02T00:00:00+08:00"
|
case dbi.DataTypeDate: // "2024-01-02T00:00:00+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateOnly)
|
return res.Format(time.DateOnly)
|
||||||
case dbi.DataTypeTime: // "0000-01-01T22:08:22.275688+08:00"
|
case dbi.DataTypeTime: // "0000-01-01T22:08:22.275688+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.TimeOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.TimeOnly)
|
return res.Format(time.TimeOnly)
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any {
|
func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any {
|
||||||
// 如果dataType是datetime而dbColumnValue是string类型,则需要转换为time.Time类型
|
// 如果dataType是datetime而dbColumnValue是string类型,则需要转换为time.Time类型
|
||||||
_, ok := dbColumnValue.(string)
|
_, ok := dbColumnValue.(string)
|
||||||
if ok {
|
if ok {
|
||||||
@@ -457,3 +492,24 @@ func (dd *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
|||||||
}
|
}
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
val = strings.Replace(val, `\''`, `\'`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
return fmt.Sprintf("'%s'", dc.FormatData(dbColumnValue, dataType))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
@@ -253,35 +253,14 @@ func (md *MssqlDialect) ToCommonColumn(dialectColumn *dbi.Column) {
|
|||||||
|
|
||||||
func (md *MssqlDialect) ToColumn(commonColumn *dbi.Column) {
|
func (md *MssqlDialect) ToColumn(commonColumn *dbi.Column) {
|
||||||
ctype := mssqlColumnTypeMap[commonColumn.DataType]
|
ctype := mssqlColumnTypeMap[commonColumn.DataType]
|
||||||
|
meta := md.dc.GetMetaData()
|
||||||
|
|
||||||
if ctype == "" {
|
if ctype == "" {
|
||||||
commonColumn.DataType = "varchar"
|
commonColumn.DataType = "varchar"
|
||||||
commonColumn.CharMaxLength = 2000
|
commonColumn.CharMaxLength = 2000
|
||||||
} else {
|
} else {
|
||||||
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
||||||
|
meta.FixColumn(commonColumn)
|
||||||
if strings.Contains(strings.ToLower(ctype), "int") {
|
|
||||||
// 如果类型是数字,类型后不需要带长度
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
commonColumn.NumPrecision = 0
|
|
||||||
} else if collx.ArrayAnyMatches([]string{"float", "number", "decimal"}, strings.ToLower(ctype)) {
|
|
||||||
// 如果是float,最大长度为38
|
|
||||||
if commonColumn.CharMaxLength > 38 {
|
|
||||||
commonColumn.CharMaxLength = 38
|
|
||||||
}
|
|
||||||
if commonColumn.NumPrecision > 38 {
|
|
||||||
commonColumn.NumPrecision = 38
|
|
||||||
}
|
|
||||||
} else if strings.Contains(strings.ToLower(ctype), "char") {
|
|
||||||
// 如果是字符串类型,长度最大4000,否则修改字段类型为text
|
|
||||||
if commonColumn.CharMaxLength > 4000 {
|
|
||||||
commonColumn.DataType = "text"
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
}
|
|
||||||
} else if strings.Contains(strings.ToLower(ctype), "text") {
|
|
||||||
// 如果是text,取消长度
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,28 +119,32 @@ func (md *MssqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
|||||||
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
dataType := strings.ToLower(string(column.DataType))
|
md.FixColumn(&column)
|
||||||
|
|
||||||
if collx.ArrayAnyMatches([]string{"date", "time"}, dataType) {
|
|
||||||
// 如果是datetime,精度取NumScale字段
|
|
||||||
column.CharMaxLength = column.NumScale
|
|
||||||
} else if collx.ArrayAnyMatches([]string{"int", "bit", "real", "text", "xml"}, dataType) {
|
|
||||||
// 不显示长度的类型
|
|
||||||
column.NumPrecision = 0
|
|
||||||
column.CharMaxLength = 0
|
|
||||||
} else if collx.ArrayAnyMatches([]string{"numeric", "decimal", "float"}, dataType) {
|
|
||||||
// 如果是num,长度取精度和小数位数
|
|
||||||
column.CharMaxLength = 0
|
|
||||||
} else if collx.ArrayAnyMatches([]string{"nvarchar", "nchar"}, dataType) {
|
|
||||||
// 如果是nvarchar,可视长度减半
|
|
||||||
column.CharMaxLength = column.CharMaxLength / 2
|
|
||||||
}
|
|
||||||
|
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (md *MssqlMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
dataType := strings.ToLower(string(column.DataType))
|
||||||
|
|
||||||
|
if collx.ArrayAnyMatches([]string{"date", "time"}, dataType) {
|
||||||
|
// 如果是datetime,精度取NumScale字段
|
||||||
|
column.CharMaxLength = column.NumScale
|
||||||
|
} else if collx.ArrayAnyMatches([]string{"int", "bit", "real", "text", "xml"}, dataType) {
|
||||||
|
// 不显示长度的类型
|
||||||
|
column.NumPrecision = 0
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
} else if collx.ArrayAnyMatches([]string{"numeric", "decimal", "float"}, dataType) {
|
||||||
|
// 如果是num,长度取精度和小数位数
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
} else if collx.ArrayAnyMatches([]string{"nvarchar", "nchar"}, dataType) {
|
||||||
|
// 如果是nvarchar,可视长度减半
|
||||||
|
column.CharMaxLength = column.CharMaxLength / 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||||
func (md *MssqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
func (md *MssqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := md.GetColumns(tablename)
|
columns, err := md.GetColumns(tablename)
|
||||||
@@ -174,6 +178,7 @@ func (md *MssqlMetaData) getTableIndexWithPK(tableName string) ([]dbi.Index, err
|
|||||||
IndexComment: cast.ToString(re["indexComment"]),
|
IndexComment: cast.ToString(re["indexComment"]),
|
||||||
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
||||||
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
||||||
|
IsPrimaryKey: cast.ToInt(re["isPrimaryKey"]) == 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,多个索引字段以逗号连接
|
// 把查询结果以索引名分组,多个索引字段以逗号连接
|
||||||
@@ -199,10 +204,9 @@ func (md *MssqlMetaData) getTableIndexWithPK(tableName string) ([]dbi.Index, err
|
|||||||
func (md *MssqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
func (md *MssqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||||
indexs, _ := md.getTableIndexWithPK(tableName)
|
indexs, _ := md.getTableIndexWithPK(tableName)
|
||||||
result := make([]dbi.Index, 0)
|
result := make([]dbi.Index, 0)
|
||||||
// 过滤掉主键索引,主键索引名为PK__开头的
|
// 过滤掉主键索引
|
||||||
for _, v := range indexs {
|
for _, v := range indexs {
|
||||||
in := v.IndexName
|
if v.IsPrimaryKey {
|
||||||
if strings.HasPrefix(in, "PK__") {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
result = append(result, v)
|
result = append(result, v)
|
||||||
@@ -248,25 +252,25 @@ func (md *MssqlMetaData) CopyTableDDL(tableName string, newTableName string) (st
|
|||||||
|
|
||||||
func (md *MssqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
func (md *MssqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
||||||
tbName := tableInfo.TableName
|
tbName := tableInfo.TableName
|
||||||
|
meta := md.dc.GetMetaData()
|
||||||
sqls := make([]string, 0)
|
sqls := make([]string, 0)
|
||||||
comments := make([]string, 0)
|
comments := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tbName, colName)
|
// 取出列名,添加引号
|
||||||
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
|
colNames := make([]string, len(cols))
|
||||||
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
|
}
|
||||||
|
|
||||||
sqls = append(sqls, fmt.Sprintf("create %s NONCLUSTERED index %s on %s.%s(%s)", unique, indexName, md.dc.Info.CurrentSchema(), tbName, index.ColumnName))
|
sqls = append(sqls, fmt.Sprintf("create %s NONCLUSTERED index %s on %s.%s(%s)", unique, index.IndexName, md.dc.Info.CurrentSchema(), tbName, strings.Join(colNames, ",")))
|
||||||
if index.IndexComment != "" {
|
if index.IndexComment != "" {
|
||||||
comments = append(comments, fmt.Sprintf("EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'INDEX', N'%s'", index.IndexComment, md.dc.Info.CurrentSchema(), tbName, indexName))
|
comment := meta.QuoteEscape(index.IndexComment)
|
||||||
|
comments = append(comments, fmt.Sprintf("EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'INDEX', N'%s'", comment, md.dc.Info.CurrentSchema(), tbName, index.IndexName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(comments) > 0 {
|
if len(comments) > 0 {
|
||||||
@@ -312,7 +316,7 @@ func (md *MssqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.GetColumnType(), incr, nullAble, defVal)
|
columnSql := fmt.Sprintf(" %s %s%s%s%s", colName, column.GetColumnType(), incr, nullAble, defVal)
|
||||||
return columnSql
|
return columnSql
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +324,6 @@ func (md *MssqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
||||||
tbName := tableInfo.TableName
|
tbName := tableInfo.TableName
|
||||||
meta := md.dc.GetMetaData()
|
meta := md.dc.GetMetaData()
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
|
||||||
|
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
|
|
||||||
@@ -344,7 +347,7 @@ func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
|
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
// 防止注释内含有特殊字符串导致sql出错
|
||||||
if column.ColumnComment != "" {
|
if column.ColumnComment != "" {
|
||||||
comment := replacer.Replace(column.ColumnComment)
|
comment := meta.QuoteEscape(column.ColumnComment)
|
||||||
columnComments = append(columnComments, fmt.Sprintf(commentTmp, comment, md.dc.Info.CurrentSchema(), tbName, column.ColumnName))
|
columnComments = append(columnComments, fmt.Sprintf(commentTmp, comment, md.dc.Info.CurrentSchema(), tbName, column.ColumnName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -360,7 +363,8 @@ func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
tableCommentSql := ""
|
tableCommentSql := ""
|
||||||
if tableInfo.TableComment != "" {
|
if tableInfo.TableComment != "" {
|
||||||
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s'"
|
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s'"
|
||||||
tableCommentSql = fmt.Sprintf(commentTmp, replacer.Replace(tableInfo.TableComment), md.dc.Info.CurrentSchema(), tbName)
|
|
||||||
|
tableCommentSql = fmt.Sprintf(commentTmp, meta.QuoteEscape(tableInfo.TableComment), md.dc.Info.CurrentSchema(), tbName)
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlArr = append(sqlArr, createSql)
|
sqlArr = append(sqlArr, createSql)
|
||||||
@@ -376,13 +380,12 @@ func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (md *MssqlMetaData) GetTableDDL(tableName string) (string, error) {
|
func (md *MssqlMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
|
|
||||||
// 1.获取表信息
|
// 1.获取表信息
|
||||||
tbs, err := md.GetTables(tableName)
|
tbs, err := md.GetTables(tableName)
|
||||||
tableInfo := &dbi.Table{}
|
tableInfo := &dbi.Table{}
|
||||||
if err != nil && len(tbs) > 0 {
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
|
|
||||||
logx.Errorf("获取表信息失败, %s", tableName)
|
logx.Errorf("获取表信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -395,7 +398,7 @@ func (md *MssqlMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
logx.Errorf("获取列信息失败, %s", tableName)
|
logx.Errorf("获取列信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, false)
|
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, dropBeforeCreate)
|
||||||
// 3.获取索引信息
|
// 3.获取索引信息
|
||||||
indexs, err := md.GetTableIndex(tableName)
|
indexs, err := md.GetTableIndex(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -424,6 +427,10 @@ func (md *MssqlMetaData) GetIdentifierQuoteString() string {
|
|||||||
return "["
|
return "["
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (md *MssqlMetaData) BeforeDumpInsertSql(quoteSchema string, tableName string) string {
|
||||||
|
return fmt.Sprintf("set identity_insert %s.%s on ", quoteSchema, tableName)
|
||||||
|
}
|
||||||
|
|
||||||
func (md *MssqlMetaData) GetDataConverter() dbi.DataConverter {
|
func (md *MssqlMetaData) GetDataConverter() dbi.DataConverter {
|
||||||
return converter
|
return converter
|
||||||
}
|
}
|
||||||
@@ -528,6 +535,26 @@ func (dc *DataConverter) GetDataType(dbColumnType string) dbi.DataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
// 如果dataType是datetime而dbColumnValue是string类型,则需要根据类型格式化
|
||||||
|
str, ok := dbColumnValue.(string)
|
||||||
|
if dataType == dbi.DataTypeDateTime && ok {
|
||||||
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
|
return res.Format(time.DateTime)
|
||||||
|
}
|
||||||
|
if dataType == dbi.DataTypeDate && ok {
|
||||||
|
// 尝试用时间格式解析
|
||||||
|
res, _ := time.Parse(time.DateOnly, str)
|
||||||
|
return res.Format(time.DateOnly)
|
||||||
|
}
|
||||||
|
if dataType == dbi.DataTypeTime && ok {
|
||||||
|
res, _ := time.Parse(time.TimeOnly, str)
|
||||||
|
return res.Format(time.TimeOnly)
|
||||||
|
}
|
||||||
return anyx.ToString(dbColumnValue)
|
return anyx.ToString(dbColumnValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -548,3 +575,24 @@ func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
|||||||
}
|
}
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
val = strings.Replace(val, `\''`, `\'`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
return fmt.Sprintf("'%s'", dc.FormatData(dbColumnValue, dataType))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
@@ -97,16 +97,7 @@ func (md *MysqlDialect) ToColumn(column *dbi.Column) {
|
|||||||
column.CharMaxLength = 1000
|
column.CharMaxLength = 1000
|
||||||
} else {
|
} else {
|
||||||
column.DataType = dbi.ColumnDataType(ctype)
|
column.DataType = dbi.ColumnDataType(ctype)
|
||||||
// 如果是int整型,删除精度
|
md.dc.GetMetaData().FixColumn(column)
|
||||||
if strings.Contains(strings.ToLower(ctype), "int") {
|
|
||||||
column.NumScale = 0
|
|
||||||
column.CharMaxLength = 0
|
|
||||||
} else
|
|
||||||
// 如果是text,删除长度
|
|
||||||
if strings.Contains(strings.ToLower(ctype), "text") {
|
|
||||||
column.CharMaxLength = 0
|
|
||||||
column.NumPrecision = 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -117,11 +117,25 @@ func (md *MysqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
|||||||
NumScale: cast.ToInt(re["numScale"]),
|
NumScale: cast.ToInt(re["numScale"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
md.FixColumn(&column)
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (md *MysqlMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
// 如果是int整型,删除精度
|
||||||
|
if strings.Contains(strings.ToLower(string(column.DataType)), "int") {
|
||||||
|
column.NumScale = 0
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
} else
|
||||||
|
// 如果是text,删除长度
|
||||||
|
if strings.Contains(strings.ToLower(string(column.DataType)), "text") {
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
column.NumPrecision = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||||
func (md *MysqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
func (md *MysqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := md.GetColumns(tablename)
|
columns, err := md.GetColumns(tablename)
|
||||||
@@ -157,6 +171,7 @@ func (md *MysqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
IndexComment: cast.ToString(re["indexComment"]),
|
IndexComment: cast.ToString(re["indexComment"]),
|
||||||
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
||||||
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
||||||
|
IsPrimaryKey: cast.ToInt(re["isPrimaryKey"]) == 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||||
@@ -183,27 +198,29 @@ func (md *MysqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Tabl
|
|||||||
meta := md.dc.GetMetaData()
|
meta := md.dc.GetMetaData()
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
|
// 取出列名,添加引号
|
||||||
sqlTmp := "ALTER TABLE %s ADD %s INDEX %s(%s) USING BTREE COMMENT '%s'"
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
colNames := make([]string, len(cols))
|
||||||
sqlArr = append(sqlArr, fmt.Sprintf(sqlTmp, meta.QuoteIdentifier(tableInfo.TableName), unique, indexName, index.ColumnName, replacer.Replace(index.IndexComment)))
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
|
}
|
||||||
|
sqlTmp := "ALTER TABLE %s ADD %s INDEX %s(%s) USING BTREE"
|
||||||
|
sqlStr := fmt.Sprintf(sqlTmp, meta.QuoteIdentifier(tableInfo.TableName), unique, index.IndexName, strings.Join(colNames, ","))
|
||||||
|
comment := meta.QuoteEscape(index.IndexComment)
|
||||||
|
if comment != "" {
|
||||||
|
sqlStr += fmt.Sprintf(" COMMENT '%s'", comment)
|
||||||
|
}
|
||||||
|
sqlArr = append(sqlArr, sqlStr)
|
||||||
}
|
}
|
||||||
return sqlArr
|
return sqlArr
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *MysqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
func (md *MysqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
meta := md.dc.GetMetaData()
|
||||||
dataType := string(column.DataType)
|
dataType := string(column.DataType)
|
||||||
|
|
||||||
incr := ""
|
incr := ""
|
||||||
@@ -238,11 +255,11 @@ func (md *MysqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
comment := ""
|
comment := ""
|
||||||
if column.ColumnComment != "" {
|
if column.ColumnComment != "" {
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
// 防止注释内含有特殊字符串导致sql出错
|
||||||
commentStr := replacer.Replace(column.ColumnComment)
|
commentStr := meta.QuoteEscape(column.ColumnComment)
|
||||||
comment = fmt.Sprintf(" COMMENT '%s'", commentStr)
|
comment = fmt.Sprintf(" COMMENT '%s'", commentStr)
|
||||||
}
|
}
|
||||||
|
|
||||||
columnSql := fmt.Sprintf(" %s %s %s %s %s %s", md.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.GetColumnType(), nullAble, incr, defVal, comment)
|
columnSql := fmt.Sprintf(" %s %s%s%s%s%s", md.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.GetColumnType(), nullAble, incr, defVal, comment)
|
||||||
return columnSql
|
return columnSql
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,12 +289,11 @@ func (md *MysqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
if len(pks) > 0 {
|
if len(pks) > 0 {
|
||||||
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
|
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
|
||||||
}
|
}
|
||||||
createSql += fmt.Sprintf(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ")
|
createSql += "\n)"
|
||||||
|
|
||||||
// 表注释
|
// 表注释
|
||||||
if tableInfo.TableComment != "" {
|
if tableInfo.TableComment != "" {
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
createSql += fmt.Sprintf(" COMMENT '%s'", meta.QuoteEscape(tableInfo.TableComment))
|
||||||
createSql += fmt.Sprintf(" COMMENT '%s'", replacer.Replace(tableInfo.TableComment))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlArr = append(sqlArr, createSql)
|
sqlArr = append(sqlArr, createSql)
|
||||||
@@ -286,11 +302,11 @@ func (md *MysqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (md *MysqlMetaData) GetTableDDL(tableName string) (string, error) {
|
func (md *MysqlMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
// 1.获取表信息
|
// 1.获取表信息
|
||||||
tbs, err := md.GetTables(tableName)
|
tbs, err := md.GetTables(tableName)
|
||||||
tableInfo := &dbi.Table{}
|
tableInfo := &dbi.Table{}
|
||||||
if err != nil && len(tbs) > 0 {
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
logx.Errorf("获取表信息失败, %s", tableName)
|
logx.Errorf("获取表信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -303,7 +319,7 @@ func (md *MysqlMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
logx.Errorf("获取列信息失败, %s", tableName)
|
logx.Errorf("获取列信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, false)
|
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, dropBeforeCreate)
|
||||||
// 3.获取索引信息
|
// 3.获取索引信息
|
||||||
indexs, err := md.GetTableIndex(tableName)
|
indexs, err := md.GetTableIndex(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -426,6 +442,25 @@ func (dc *DataConverter) GetDataType(dbColumnType string) dbi.DataType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
// 如果dataType是datetime而dbColumnValue是string类型,则需要根据类型格式化
|
||||||
|
str, ok := dbColumnValue.(string)
|
||||||
|
if dataType == dbi.DataTypeDateTime && ok {
|
||||||
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
|
return res.Format(time.DateTime)
|
||||||
|
}
|
||||||
|
if dataType == dbi.DataTypeDate && ok {
|
||||||
|
res, _ := time.Parse(time.DateOnly, str)
|
||||||
|
return res.Format(time.DateOnly)
|
||||||
|
}
|
||||||
|
if dataType == dbi.DataTypeTime && ok {
|
||||||
|
res, _ := time.Parse(time.TimeOnly, str)
|
||||||
|
return res.Format(time.TimeOnly)
|
||||||
|
}
|
||||||
return anyx.ToString(dbColumnValue)
|
return anyx.ToString(dbColumnValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,3 +483,26 @@ func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
|||||||
}
|
}
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
val = strings.Replace(val, `\''`, `\'`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
// mysql时间类型无需格式化
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
@@ -178,17 +178,7 @@ func (od *OracleDialect) ToColumn(commonColumn *dbi.Column) {
|
|||||||
commonColumn.CharMaxLength = 2000
|
commonColumn.CharMaxLength = 2000
|
||||||
} else {
|
} else {
|
||||||
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
||||||
// 如果类型是数字,类型后不需要带长度
|
od.dc.GetMetaData().FixColumn(commonColumn)
|
||||||
if strings.Contains(strings.ToLower(ctype), "int") {
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
commonColumn.NumPrecision = 0
|
|
||||||
} else if strings.Contains(strings.ToLower(ctype), "char") {
|
|
||||||
// 如果是字符串类型,长度最大4000,否则修改字段类型为clob
|
|
||||||
if commonColumn.CharMaxLength > 4000 {
|
|
||||||
commonColumn.DataType = "CLOB"
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -120,28 +120,47 @@ func (od *OracleMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
|||||||
|
|
||||||
columns := make([]dbi.Column, 0)
|
columns := make([]dbi.Column, 0)
|
||||||
for _, re := range res {
|
for _, re := range res {
|
||||||
defaultVal := cast.ToString(re["COLUMN_DEFAULT"])
|
|
||||||
// 如果默认值包含.nextval,说明是序列,默认值为null
|
|
||||||
if strings.Contains(defaultVal, ".nextval") {
|
|
||||||
defaultVal = ""
|
|
||||||
}
|
|
||||||
column := dbi.Column{
|
column := dbi.Column{
|
||||||
TableName: cast.ToString(re["TABLE_NAME"]),
|
TableName: cast.ToString(re["TABLE_NAME"]),
|
||||||
ColumnName: cast.ToString(re["COLUMN_NAME"]),
|
ColumnName: cast.ToString(re["COLUMN_NAME"]),
|
||||||
DataType: dbi.ColumnDataType(cast.ToString(re["DATA_TYPE"])),
|
DataType: dbi.ColumnDataType(cast.ToString(re["DATA_TYPE"])),
|
||||||
|
CharMaxLength: cast.ToInt(re["CHAR_MAX_LENGTH"]),
|
||||||
ColumnComment: cast.ToString(re["COLUMN_COMMENT"]),
|
ColumnComment: cast.ToString(re["COLUMN_COMMENT"]),
|
||||||
Nullable: cast.ToString(re["NULLABLE"]) == "YES",
|
Nullable: cast.ToString(re["NULLABLE"]) == "YES",
|
||||||
IsPrimaryKey: cast.ToInt(re["IS_PRIMARY_KEY"]) == 1,
|
IsPrimaryKey: cast.ToInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||||
IsIdentity: cast.ToInt(re["IS_IDENTITY"]) == 1,
|
IsIdentity: cast.ToInt(re["IS_IDENTITY"]) == 1,
|
||||||
ColumnDefault: defaultVal,
|
ColumnDefault: cast.ToString(re["COLUMN_DEFAULT"]),
|
||||||
|
NumPrecision: cast.ToInt(re["NUM_PRECISION"]),
|
||||||
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
NumScale: cast.ToInt(re["NUM_SCALE"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
od.FixColumn(&column)
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (od *OracleMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
// 如果默认值包含.nextval,说明是序列,默认值为null
|
||||||
|
if strings.Contains(column.ColumnDefault, ".nextval") {
|
||||||
|
column.ColumnDefault = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一处理一下数据类型的长度
|
||||||
|
if collx.ArrayAnyMatches([]string{"date", "time", "lob", "int"}, strings.ToLower(string(column.DataType))) {
|
||||||
|
// 如果是不需要设置长度的类型
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
column.NumPrecision = 0
|
||||||
|
} else if strings.Contains(strings.ToLower(string(column.DataType)), "char") {
|
||||||
|
// 如果是字符串类型,长度最大4000,否则修改字段类型为clob
|
||||||
|
if column.CharMaxLength > 4000 {
|
||||||
|
column.DataType = "NCLOB"
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
column.NumPrecision = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (od *OracleMetaData) GetPrimaryKey(tablename string) (string, error) {
|
func (od *OracleMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := od.GetColumns(tablename)
|
columns, err := od.GetColumns(tablename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -175,6 +194,7 @@ func (od *OracleMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
IndexComment: cast.ToString(re["INDEX_COMMENT"]),
|
IndexComment: cast.ToString(re["INDEX_COMMENT"]),
|
||||||
IsUnique: cast.ToInt(re["IS_UNIQUE"]) == 1,
|
IsUnique: cast.ToInt(re["IS_UNIQUE"]) == 1,
|
||||||
SeqInIndex: cast.ToInt(re["SEQ_IN_INDEX"]),
|
SeqInIndex: cast.ToInt(re["SEQ_IN_INDEX"]),
|
||||||
|
IsPrimaryKey: cast.ToInt(re["IS_PRIMARY"]) == 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||||
@@ -204,23 +224,19 @@ func (od *OracleMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Tab
|
|||||||
comments := make([]string, 0)
|
comments := make([]string, 0)
|
||||||
|
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
|
|
||||||
|
|
||||||
sqls = append(sqls, fmt.Sprintf("CREATE %s INDEX %s ON %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
|
// 取出列名,添加引号
|
||||||
if index.IndexComment != "" {
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s IS '%s'", indexName, index.IndexComment))
|
colNames := make([]string, len(cols))
|
||||||
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sqls = append(sqls, fmt.Sprintf("CREATE %s INDEX %s ON %s(%s)", unique, index.IndexName, meta.QuoteIdentifier(tableInfo.TableName), strings.Join(colNames, ",")))
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
@@ -237,7 +253,6 @@ func (od *OracleMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Tab
|
|||||||
func (od *OracleMetaData) genColumnBasicSql(column dbi.Column) string {
|
func (od *OracleMetaData) genColumnBasicSql(column dbi.Column) string {
|
||||||
meta := od.dc.GetMetaData()
|
meta := od.dc.GetMetaData()
|
||||||
colName := meta.QuoteIdentifier(column.ColumnName)
|
colName := meta.QuoteIdentifier(column.ColumnName)
|
||||||
dataType := string(column.DataType)
|
|
||||||
|
|
||||||
if column.IsIdentity {
|
if column.IsIdentity {
|
||||||
// 如果是自增,不需要设置默认值和空值,自增列数据类型必须是number
|
// 如果是自增,不需要设置默认值和空值,自增列数据类型必须是number
|
||||||
@@ -249,47 +264,18 @@ func (od *OracleMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
nullAble = " NOT NULL"
|
nullAble = " NOT NULL"
|
||||||
}
|
}
|
||||||
|
|
||||||
defVal := "" // 默认值需要判断引号,如函数是不需要引号的
|
defVal := ""
|
||||||
if column.ColumnDefault != "" {
|
if column.ColumnDefault != "" {
|
||||||
mark := false
|
defVal = fmt.Sprintf(" DEFAULT %v", column.ColumnDefault)
|
||||||
// 哪些字段类型默认值需要加引号
|
|
||||||
if collx.ArrayAnyMatches([]string{"CHAR", "LONG", "DATE", "TIME", "CLOB", "BLOB", "BFILE"}, dataType) {
|
|
||||||
// 默认值是时间日期函数的必须要加引号
|
|
||||||
val := strings.ToUpper(column.ColumnDefault)
|
|
||||||
if collx.ArrayAnyMatches([]string{"DATE", "TIMESTAMP"}, dataType) && val == "CURRENT_DATE" || val == "CURRENT_TIMESTAMP" {
|
|
||||||
mark = false
|
|
||||||
} else {
|
|
||||||
mark = true
|
|
||||||
}
|
|
||||||
if mark {
|
|
||||||
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
|
|
||||||
} else {
|
|
||||||
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 如果是数字,默认值提取数字
|
|
||||||
if collx.ArrayAnyMatches([]string{"NUM", "INT"}, dataType) {
|
|
||||||
match := bracketsRegexp.FindStringSubmatch(dataType)
|
|
||||||
if len(match) > 1 {
|
|
||||||
length := cast.ToInt(match[1])
|
|
||||||
defVal = fmt.Sprintf(" DEFAULT %d", length)
|
|
||||||
} else {
|
|
||||||
defVal = fmt.Sprintf(" DEFAULT 0")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
columnSql := fmt.Sprintf(" %s %s %s %s", colName, column.GetColumnType(), defVal, nullAble)
|
columnSql := fmt.Sprintf(" %s %s%s%s", colName, column.GetColumnType(), defVal, nullAble)
|
||||||
return columnSql
|
return columnSql
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (od *OracleMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
func (od *OracleMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
||||||
meta := od.dc.GetMetaData()
|
meta := od.dc.GetMetaData()
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
|
||||||
quoteTableName := meta.QuoteIdentifier(tableInfo.TableName)
|
quoteTableName := meta.QuoteIdentifier(tableInfo.TableName)
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
|
|
||||||
@@ -302,8 +288,7 @@ begin
|
|||||||
if num > 0 then
|
if num > 0 then
|
||||||
execute immediate 'drop table "%s"' ;
|
execute immediate 'drop table "%s"' ;
|
||||||
end if;
|
end if;
|
||||||
end;
|
end`
|
||||||
`
|
|
||||||
sqlArr = append(sqlArr, fmt.Sprintf(dropSqlTmp, tableInfo.TableName, tableInfo.TableName))
|
sqlArr = append(sqlArr, fmt.Sprintf(dropSqlTmp, tableInfo.TableName, tableInfo.TableName))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,7 +305,7 @@ end;
|
|||||||
fields = append(fields, od.genColumnBasicSql(column))
|
fields = append(fields, od.genColumnBasicSql(column))
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
// 防止注释内含有特殊字符串导致sql出错
|
||||||
if column.ColumnComment != "" {
|
if column.ColumnComment != "" {
|
||||||
comment := replacer.Replace(column.ColumnComment)
|
comment := meta.QuoteEscape(column.ColumnComment)
|
||||||
columnComments = append(columnComments, fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoteTableName, meta.QuoteIdentifier(column.ColumnName), comment))
|
columnComments = append(columnComments, fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoteTableName, meta.QuoteIdentifier(column.ColumnName), comment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -336,7 +321,7 @@ end;
|
|||||||
// 表注释
|
// 表注释
|
||||||
tableCommentSql := ""
|
tableCommentSql := ""
|
||||||
if tableInfo.TableComment != "" {
|
if tableInfo.TableComment != "" {
|
||||||
tableCommentSql = fmt.Sprintf("COMMENT ON TABLE %s is '%s'", meta.QuoteIdentifier(tableInfo.TableName), replacer.Replace(tableInfo.TableComment))
|
tableCommentSql = fmt.Sprintf("COMMENT ON TABLE %s is '%s'", meta.QuoteIdentifier(tableInfo.TableName), meta.QuoteEscape(tableInfo.TableComment))
|
||||||
sqlArr = append(sqlArr, tableCommentSql)
|
sqlArr = append(sqlArr, tableCommentSql)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -349,13 +334,12 @@ end;
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (od *OracleMetaData) GetTableDDL(tableName string) (string, error) {
|
func (od *OracleMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
|
|
||||||
// 1.获取表信息
|
// 1.获取表信息
|
||||||
tbs, err := od.GetTables(tableName)
|
tbs, err := od.GetTables(tableName)
|
||||||
tableInfo := &dbi.Table{}
|
tableInfo := &dbi.Table{}
|
||||||
if err != nil && len(tbs) > 0 {
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
|
|
||||||
logx.Errorf("获取表信息失败, %s", tableName)
|
logx.Errorf("获取表信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -368,7 +352,7 @@ func (od *OracleMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
logx.Errorf("获取列信息失败, %s", tableName)
|
logx.Errorf("获取列信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tableDDLArr := od.GenerateTableDDL(columns, *tableInfo, false)
|
tableDDLArr := od.GenerateTableDDL(columns, *tableInfo, dropBeforeCreate)
|
||||||
// 3.获取索引信息
|
// 3.获取索引信息
|
||||||
indexs, err := od.GetTableIndex(tableName)
|
indexs, err := od.GetTableIndex(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -476,7 +460,12 @@ func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) st
|
|||||||
switch dataType {
|
switch dataType {
|
||||||
// oracle把日期类型数据格式化输出
|
// oracle把日期类型数据格式化输出
|
||||||
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateTime)
|
return res.Format(time.DateTime)
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
@@ -490,3 +479,24 @@ func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
|||||||
}
|
}
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
val = strings.Replace(val, `\''`, `\'`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
return fmt.Sprintf("to_timestamp('%s', 'yyyy-mm-dd hh24:mi:ss')", dc.FormatData(dbColumnValue, dataType))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
@@ -197,14 +197,7 @@ func (pd *PgsqlDialect) ToColumn(commonColumn *dbi.Column) {
|
|||||||
commonColumn.CharMaxLength = 2000
|
commonColumn.CharMaxLength = 2000
|
||||||
} else {
|
} else {
|
||||||
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
commonColumn.DataType = dbi.ColumnDataType(ctype)
|
||||||
// 哪些字段可以指定长度
|
|
||||||
if !collx.ArrayAnyMatches([]string{"char", "time", "bit", "num", "decimal"}, ctype) {
|
|
||||||
commonColumn.CharMaxLength = 0
|
|
||||||
commonColumn.NumPrecision = 0
|
|
||||||
} else if strings.Contains(strings.ToLower(ctype), "char") {
|
|
||||||
// 如果类型是文本,长度翻倍
|
|
||||||
commonColumn.CharMaxLength = commonColumn.CharMaxLength * 2
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package postgres
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
@@ -114,12 +115,31 @@ func (pd *PgsqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
|||||||
NumPrecision: cast.ToInt(re["numPrecision"]),
|
NumPrecision: cast.ToInt(re["numPrecision"]),
|
||||||
NumScale: cast.ToInt(re["numScale"]),
|
NumScale: cast.ToInt(re["numScale"]),
|
||||||
}
|
}
|
||||||
|
pd.FixColumn(&column)
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pd *PgsqlMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
dataType := strings.ToLower(string(column.DataType))
|
||||||
|
// 哪些字段可以指定长度
|
||||||
|
if !collx.ArrayAnyMatches([]string{"char", "time", "bit", "num", "decimal"}, dataType) {
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
column.NumPrecision = 0
|
||||||
|
} else if strings.Contains(dataType, "char") {
|
||||||
|
// 如果类型是文本,长度翻倍
|
||||||
|
column.CharMaxLength = column.CharMaxLength * 2
|
||||||
|
}
|
||||||
|
// 如果默认值带冒号,如:'id'::varchar
|
||||||
|
if column.ColumnDefault != "" && strings.Contains(column.ColumnDefault, "::") && !strings.HasPrefix(column.ColumnDefault, "nextval") {
|
||||||
|
match := defaultValueRegexp.FindStringSubmatch(column.ColumnDefault)
|
||||||
|
if len(match) > 1 {
|
||||||
|
column.ColumnDefault = match[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (pd *PgsqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
func (pd *PgsqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := pd.GetColumns(tablename)
|
columns, err := pd.GetColumns(tablename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -153,6 +173,7 @@ func (pd *PgsqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
IndexComment: cast.ToString(re["indexComment"]),
|
IndexComment: cast.ToString(re["indexComment"]),
|
||||||
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
IsUnique: cast.ToInt(re["isUnique"]) == 1,
|
||||||
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
SeqInIndex: cast.ToInt(re["seqInIndex"]),
|
||||||
|
IsPrimaryKey: cast.ToInt(re["isPrimaryKey"]) == 1,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||||
@@ -175,30 +196,30 @@ func (pd *PgsqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (pd *PgsqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
func (pd *PgsqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
||||||
|
meta := pd.dc.GetMetaData()
|
||||||
creates := make([]string, 0)
|
creates := make([]string, 0)
|
||||||
drops := make([]string, 0)
|
drops := make([]string, 0)
|
||||||
comments := make([]string, 0)
|
comments := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
|
|
||||||
|
|
||||||
// 如果索引名存在,先删除索引
|
// 如果索引名存在,先删除索引
|
||||||
drops = append(drops, fmt.Sprintf("drop index if exists %s.%s", pd.dc.Info.CurrentSchema(), indexName))
|
drops = append(drops, fmt.Sprintf("drop index if exists %s.%s", pd.dc.Info.CurrentSchema(), index.IndexName))
|
||||||
|
|
||||||
|
// 取出列名,添加引号
|
||||||
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
|
colNames := make([]string, len(cols))
|
||||||
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
|
}
|
||||||
// 创建索引
|
// 创建索引
|
||||||
creates = append(creates, fmt.Sprintf("CREATE %s INDEX %s on %s.%s(%s)", unique, indexName, pd.dc.Info.CurrentSchema(), tableInfo.TableName, index.ColumnName))
|
creates = append(creates, fmt.Sprintf("CREATE %s INDEX %s on %s.%s(%s)", unique, index.IndexName, pd.dc.Info.CurrentSchema(), tableInfo.TableName, strings.Join(colNames, ",")))
|
||||||
if index.IndexComment != "" {
|
if index.IndexComment != "" {
|
||||||
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s.%s IS '%s'", pd.dc.Info.CurrentSchema(), indexName, index.IndexComment))
|
comment := meta.QuoteEscape(index.IndexComment)
|
||||||
|
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s.%s IS '%s'", pd.dc.Info.CurrentSchema(), index.IndexName, comment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,6 +243,12 @@ func (pd *PgsqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
colName := meta.QuoteIdentifier(column.ColumnName)
|
colName := meta.QuoteIdentifier(column.ColumnName)
|
||||||
dataType := string(column.DataType)
|
dataType := string(column.DataType)
|
||||||
|
|
||||||
|
// 如果数据类型是数字,则去掉长度
|
||||||
|
if collx.ArrayAnyMatches([]string{"int"}, strings.ToLower(dataType)) {
|
||||||
|
column.NumPrecision = 0
|
||||||
|
column.CharMaxLength = 0
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是自增类型,需要转换为serial
|
// 如果是自增类型,需要转换为serial
|
||||||
if column.IsIdentity {
|
if column.IsIdentity {
|
||||||
if dataType == "int4" {
|
if dataType == "int4" {
|
||||||
@@ -240,31 +267,16 @@ func (pd *PgsqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
nullAble := ""
|
nullAble := ""
|
||||||
if !column.Nullable {
|
if !column.Nullable {
|
||||||
nullAble = " NOT NULL"
|
nullAble = " NOT NULL"
|
||||||
// 如果字段不能为空,则设置默认值
|
|
||||||
if column.ColumnDefault == "" {
|
|
||||||
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, strings.ToLower(dataType)) {
|
|
||||||
// 文本默认值为空字符串
|
|
||||||
column.ColumnDefault = " "
|
|
||||||
} else if collx.ArrayAnyMatches([]string{"int", "num"}, strings.ToLower(dataType)) {
|
|
||||||
// 数字默认值为0
|
|
||||||
column.ColumnDefault = "0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
|
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
|
||||||
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
|
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
|
||||||
// 哪些字段类型默认值需要加引号
|
|
||||||
mark := false
|
mark := false
|
||||||
|
// 哪些字段类型默认值需要加引号
|
||||||
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, dataType) {
|
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, dataType) {
|
||||||
// 如果是文本类型,则默认值不能带括号
|
|
||||||
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, dataType) {
|
|
||||||
column.ColumnDefault = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
|
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
|
||||||
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
|
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
|
||||||
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
|
collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnDefault)) {
|
||||||
mark = false
|
mark = false
|
||||||
} else {
|
} else {
|
||||||
mark = true
|
mark = true
|
||||||
@@ -275,30 +287,31 @@ func (pd *PgsqlMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
column.ColumnDefault = "CURRENT_TIMESTAMP"
|
column.ColumnDefault = "CURRENT_TIMESTAMP"
|
||||||
}
|
}
|
||||||
|
|
||||||
if column.ColumnDefault != "" {
|
if mark {
|
||||||
if mark {
|
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
|
||||||
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
|
} else {
|
||||||
} else {
|
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
|
||||||
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
columnSql := fmt.Sprintf(" %s %s %s %s ", colName, column.GetColumnType(), nullAble, defVal)
|
// 如果是varchar,长度翻倍,防止报错
|
||||||
|
if collx.ArrayAnyMatches([]string{"char"}, strings.ToLower(dataType)) {
|
||||||
|
column.CharMaxLength = column.CharMaxLength * 2
|
||||||
|
}
|
||||||
|
columnSql := fmt.Sprintf(" %s %s%s%s", colName, column.GetColumnType(), nullAble, defVal)
|
||||||
return columnSql
|
return columnSql
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
|
||||||
|
|
||||||
meta := pd.dc.GetMetaData()
|
meta := pd.dc.GetMetaData()
|
||||||
replacer := strings.NewReplacer(";", "", "'", "")
|
quoteTableName := meta.QuoteIdentifier(tableInfo.TableName)
|
||||||
|
|
||||||
sqlArr := make([]string, 0)
|
sqlArr := make([]string, 0)
|
||||||
if dropBeforeCreate {
|
if dropBeforeCreate {
|
||||||
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s", meta.QuoteIdentifier(tableInfo.TableName)))
|
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s", quoteTableName))
|
||||||
}
|
}
|
||||||
// 组装建表语句
|
// 组装建表语句
|
||||||
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tableInfo.TableName))
|
createSql := fmt.Sprintf("CREATE TABLE %s (\n", quoteTableName)
|
||||||
fields := make([]string, 0)
|
fields := make([]string, 0)
|
||||||
pks := make([]string, 0)
|
pks := make([]string, 0)
|
||||||
columnComments := make([]string, 0)
|
columnComments := make([]string, 0)
|
||||||
@@ -313,8 +326,8 @@ func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
|
|
||||||
// 防止注释内含有特殊字符串导致sql出错
|
// 防止注释内含有特殊字符串导致sql出错
|
||||||
if column.ColumnComment != "" {
|
if column.ColumnComment != "" {
|
||||||
comment := replacer.Replace(column.ColumnComment)
|
comment := meta.QuoteEscape(column.ColumnComment)
|
||||||
columnComments = append(columnComments, fmt.Sprintf(commentTmp, column.TableName, column.ColumnName, comment))
|
columnComments = append(columnComments, fmt.Sprintf(commentTmp, quoteTableName, meta.QuoteIdentifier(column.ColumnName), comment))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,12 +335,12 @@ func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
if len(pks) > 0 {
|
if len(pks) > 0 {
|
||||||
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
|
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
|
||||||
}
|
}
|
||||||
createSql += ")"
|
createSql += "\n)"
|
||||||
|
|
||||||
tableCommentSql := ""
|
tableCommentSql := ""
|
||||||
if tableInfo.TableComment != "" {
|
if tableInfo.TableComment != "" {
|
||||||
commentTmp := "comment on table %s is '%s'"
|
commentTmp := "comment on table %s is '%s'"
|
||||||
tableCommentSql = fmt.Sprintf(commentTmp, tableInfo.TableName, replacer.Replace(tableInfo.TableComment))
|
tableCommentSql = fmt.Sprintf(commentTmp, quoteTableName, meta.QuoteEscape(tableInfo.TableComment))
|
||||||
}
|
}
|
||||||
|
|
||||||
// create
|
// create
|
||||||
@@ -346,13 +359,12 @@ func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Ta
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (pd *PgsqlMetaData) GetTableDDL(tableName string) (string, error) {
|
func (pd *PgsqlMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
|
|
||||||
// 1.获取表信息
|
// 1.获取表信息
|
||||||
tbs, err := pd.GetTables(tableName)
|
tbs, err := pd.GetTables(tableName)
|
||||||
tableInfo := &dbi.Table{}
|
tableInfo := &dbi.Table{}
|
||||||
if err != nil && len(tbs) > 0 {
|
if err != nil || tbs == nil || len(tbs) <= 0 {
|
||||||
|
|
||||||
logx.Errorf("获取表信息失败, %s", tableName)
|
logx.Errorf("获取表信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -365,7 +377,7 @@ func (pd *PgsqlMetaData) GetTableDDL(tableName string) (string, error) {
|
|||||||
logx.Errorf("获取列信息失败, %s", tableName)
|
logx.Errorf("获取列信息失败, %s", tableName)
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
tableDDLArr := pd.GenerateTableDDL(columns, *tableInfo, false)
|
tableDDLArr := pd.GenerateTableDDL(columns, *tableInfo, dropBeforeCreate)
|
||||||
// 3.获取索引信息
|
// 3.获取索引信息
|
||||||
indexs, err := pd.GetTableIndex(tableName)
|
indexs, err := pd.GetTableIndex(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -404,6 +416,19 @@ func (pd *PgsqlMetaData) DefaultDb() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pd *PgsqlMetaData) AfterDumpInsert(writer io.Writer, tableName string, columns []dbi.Column) {
|
||||||
|
|
||||||
|
// 设置自增序列当前值
|
||||||
|
for _, column := range columns {
|
||||||
|
if column.IsIdentity {
|
||||||
|
seq := fmt.Sprintf("SELECT setval('%s_%s_seq', (SELECT max(%s) FROM %s));\n", tableName, column.ColumnName, column.ColumnName, tableName)
|
||||||
|
writer.Write([]byte(seq))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.Write([]byte("COMMIT;\n"))
|
||||||
|
}
|
||||||
|
|
||||||
func (pd *PgsqlMetaData) GetDataConverter() dbi.DataConverter {
|
func (pd *PgsqlMetaData) GetDataConverter() dbi.DataConverter {
|
||||||
return converter
|
return converter
|
||||||
}
|
}
|
||||||
@@ -417,8 +442,9 @@ var (
|
|||||||
dateRegexp = regexp.MustCompile(`(?i)date`)
|
dateRegexp = regexp.MustCompile(`(?i)date`)
|
||||||
// 时间类型
|
// 时间类型
|
||||||
timeRegexp = regexp.MustCompile(`(?i)time`)
|
timeRegexp = regexp.MustCompile(`(?i)time`)
|
||||||
// 定义正则表达式,匹配括号内的数字
|
|
||||||
bracketsRegexp = regexp.MustCompile(`\((\d+)\)`)
|
// 提取pg默认值, 如:'id'::varchar 提取id ; '-1'::integer 提取-1
|
||||||
|
defaultValueRegexp = regexp.MustCompile(`'([^']*)'`)
|
||||||
|
|
||||||
converter = new(DataConverter)
|
converter = new(DataConverter)
|
||||||
|
|
||||||
@@ -497,13 +523,28 @@ func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) st
|
|||||||
str := fmt.Sprintf("%v", dbColumnValue)
|
str := fmt.Sprintf("%v", dbColumnValue)
|
||||||
switch dataType {
|
switch dataType {
|
||||||
case dbi.DataTypeDateTime: // "2024-01-02T22:16:28.545377+08:00"
|
case dbi.DataTypeDateTime: // "2024-01-02T22:16:28.545377+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, err = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateTime)
|
return res.Format(time.DateTime)
|
||||||
case dbi.DataTypeDate: // "2024-01-02T00:00:00Z"
|
case dbi.DataTypeDate: // "2024-01-02T00:00:00Z"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateOnly)
|
return res.Format(time.DateOnly)
|
||||||
case dbi.DataTypeTime: // "0000-01-01T22:16:28.545075+08:00"
|
case dbi.DataTypeTime: // "0000-01-01T22:16:28.545075+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.TimeOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.TimeOnly)
|
return res.Format(time.TimeOnly)
|
||||||
}
|
}
|
||||||
return cast.ToString(dbColumnValue)
|
return cast.ToString(dbColumnValue)
|
||||||
@@ -526,3 +567,23 @@ func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
|||||||
}
|
}
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
return fmt.Sprintf("'%s'", dc.FormatData(dbColumnValue, dataType))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func (sd *SqliteDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
|||||||
|
|
||||||
// 生成新表名,为老表明+_copy_时间戳
|
// 生成新表名,为老表明+_copy_时间戳
|
||||||
newTableName := tableName + "_copy_" + time.Now().Format("20060102150405")
|
newTableName := tableName + "_copy_" + time.Now().Format("20060102150405")
|
||||||
ddl, err := sd.dc.GetMetaData().GetTableDDL(tableName)
|
ddl, err := sd.dc.GetMetaData().GetTableDDL(tableName, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -103,6 +103,8 @@ func (sd *SqliteDialect) ToColumn(commonColumn *dbi.Column) {
|
|||||||
if ctype == "" {
|
if ctype == "" {
|
||||||
commonColumn.DataType = "nvarchar"
|
commonColumn.DataType = "nvarchar"
|
||||||
commonColumn.CharMaxLength = 2000
|
commonColumn.CharMaxLength = 2000
|
||||||
|
} else {
|
||||||
|
sd.dc.GetMetaData().FixColumn(commonColumn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package sqlite
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
@@ -138,12 +139,17 @@ func (sd *SqliteMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
|
|||||||
}
|
}
|
||||||
column.DataType = dbi.ColumnDataType(dataType)
|
column.DataType = dbi.ColumnDataType(dataType)
|
||||||
|
|
||||||
|
sd.FixColumn(&column)
|
||||||
|
|
||||||
columns = append(columns, column)
|
columns = append(columns, column)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sd *SqliteMetaData) FixColumn(column *dbi.Column) {
|
||||||
|
}
|
||||||
|
|
||||||
func (sd *SqliteMetaData) GetPrimaryKey(tableName string) (string, error) {
|
func (sd *SqliteMetaData) GetPrimaryKey(tableName string) (string, error) {
|
||||||
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -193,6 +199,7 @@ func (sd *SqliteMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
IndexComment: cast.ToString(re["indexComment"]),
|
IndexComment: cast.ToString(re["indexComment"]),
|
||||||
IsUnique: isUnique,
|
IsUnique: isUnique,
|
||||||
SeqInIndex: 1,
|
SeqInIndex: 1,
|
||||||
|
IsPrimaryKey: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||||
@@ -201,22 +208,24 @@ func (sd *SqliteMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
|||||||
|
|
||||||
// 获取建索引ddl
|
// 获取建索引ddl
|
||||||
func (sd *SqliteMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
func (sd *SqliteMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
|
||||||
|
meta := sd.dc.GetMetaData()
|
||||||
sqls := make([]string, 0)
|
sqls := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
// 通过字段、表名拼接索引名
|
|
||||||
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
|
|
||||||
columnName = strings.ReplaceAll(columnName, "_", "")
|
|
||||||
colName := strings.ReplaceAll(columnName, ",", "_")
|
|
||||||
|
|
||||||
keyType := "normal"
|
|
||||||
unique := ""
|
unique := ""
|
||||||
if index.IsUnique {
|
if index.IsUnique {
|
||||||
keyType = "unique"
|
|
||||||
unique = "unique"
|
unique = "unique"
|
||||||
}
|
}
|
||||||
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
|
// 取出列名,添加引号
|
||||||
|
cols := strings.Split(index.ColumnName, ",")
|
||||||
|
colNames := make([]string, len(cols))
|
||||||
|
for i, name := range cols {
|
||||||
|
colNames[i] = meta.QuoteIdentifier(name)
|
||||||
|
}
|
||||||
|
// 创建前尝试删除
|
||||||
|
sqls = append(sqls, fmt.Sprintf("DROP INDEX IF EXISTS \"%s\"", index.IndexName))
|
||||||
|
|
||||||
sqlTmp := "CREATE %s INDEX %s ON \"%s\" (%s) "
|
sqlTmp := "CREATE %s INDEX %s ON \"%s\" (%s) "
|
||||||
sqls = append(sqls, fmt.Sprintf(sqlTmp, unique, indexName, tableInfo.TableName, index.ColumnName))
|
sqls = append(sqls, fmt.Sprintf(sqlTmp, unique, index.IndexName, tableInfo.TableName, strings.Join(colNames, ",")))
|
||||||
}
|
}
|
||||||
return sqls
|
return sqls
|
||||||
}
|
}
|
||||||
@@ -232,9 +241,11 @@ func (sd *SqliteMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
nullAble = " NOT NULL"
|
nullAble = " NOT NULL"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
quoteColumnName := sd.dc.GetMetaData().QuoteIdentifier(column.ColumnName)
|
||||||
|
|
||||||
// 如果是主键,则直接返回,不判断默认值
|
// 如果是主键,则直接返回,不判断默认值
|
||||||
if column.IsPrimaryKey {
|
if column.IsPrimaryKey {
|
||||||
return fmt.Sprintf(" %s integer PRIMARY KEY %s %s", column.ColumnName, incr, nullAble)
|
return fmt.Sprintf(" %s integer PRIMARY KEY%s%s", quoteColumnName, incr, nullAble)
|
||||||
}
|
}
|
||||||
|
|
||||||
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
|
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
|
||||||
@@ -257,7 +268,7 @@ func (sd *SqliteMetaData) genColumnBasicSql(column dbi.Column) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf(" %s %s %s %s", sd.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.GetColumnType(), nullAble, defVal)
|
return fmt.Sprintf(" %s %s%s%s", quoteColumnName, column.GetColumnType(), nullAble, defVal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
@@ -275,8 +286,8 @@ func (sd *SqliteMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.T
|
|||||||
for _, column := range columns {
|
for _, column := range columns {
|
||||||
fields = append(fields, sd.genColumnBasicSql(column))
|
fields = append(fields, sd.genColumnBasicSql(column))
|
||||||
}
|
}
|
||||||
createSql += strings.Join(fields, ",")
|
createSql += strings.Join(fields, ",\n")
|
||||||
createSql += ") "
|
createSql += "\n)"
|
||||||
|
|
||||||
sqlArr = append(sqlArr, createSql)
|
sqlArr = append(sqlArr, createSql)
|
||||||
|
|
||||||
@@ -284,12 +295,18 @@ func (sd *SqliteMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.T
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (sd *SqliteMetaData) GetTableDDL(tableName string) (string, error) {
|
func (sd *SqliteMetaData) GetTableDDL(tableName string, dropBeforeCreate bool) (string, error) {
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
if dropBeforeCreate {
|
||||||
|
builder.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS %s; \n\n", tableName))
|
||||||
|
}
|
||||||
|
|
||||||
_, res, err := sd.dc.Query("select sql from sqlite_master WHERE tbl_name=? order by type desc", tableName)
|
_, res, err := sd.dc.Query("select sql from sqlite_master WHERE tbl_name=? order by type desc", tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
var builder strings.Builder
|
|
||||||
for _, re := range res {
|
for _, re := range res {
|
||||||
builder.WriteString(cast.ToString(re["sql"]) + "; \n\n")
|
builder.WriteString(cast.ToString(re["sql"]) + "; \n\n")
|
||||||
}
|
}
|
||||||
@@ -305,6 +322,11 @@ func (sd *SqliteMetaData) GetDataConverter() dbi.DataConverter {
|
|||||||
return converter
|
return converter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sd *SqliteMetaData) BeforeDumpInsert(writer io.Writer, tableName string) {
|
||||||
|
}
|
||||||
|
func (sd *SqliteMetaData) AfterDumpInsert(writer io.Writer, tableName string, columns []dbi.Column) {
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// 数字类型
|
// 数字类型
|
||||||
numberRegexp = regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit|real`)
|
numberRegexp = regexp.MustCompile(`(?i)int|double|float|number|decimal|byte|bit|real`)
|
||||||
@@ -388,13 +410,28 @@ func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) st
|
|||||||
str := anyx.ToString(dbColumnValue)
|
str := anyx.ToString(dbColumnValue)
|
||||||
switch dataType {
|
switch dataType {
|
||||||
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
case dbi.DataTypeDateTime: // "2024-01-02T22:08:22.275697+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateTime, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateTime)
|
return res.Format(time.DateTime)
|
||||||
case dbi.DataTypeDate: // "2024-01-02T00:00:00+08:00"
|
case dbi.DataTypeDate: // "2024-01-02T00:00:00+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.DateOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.DateOnly)
|
return res.Format(time.DateOnly)
|
||||||
case dbi.DataTypeTime: // "0000-01-01T22:08:22.275688+08:00"
|
case dbi.DataTypeTime: // "0000-01-01T22:08:22.275688+08:00"
|
||||||
res, _ := time.Parse(time.RFC3339, str)
|
// 尝试用时间格式解析
|
||||||
|
res, err := time.Parse(time.TimeOnly, str)
|
||||||
|
if err == nil {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
res, _ = time.Parse(time.RFC3339, str)
|
||||||
return res.Format(time.TimeOnly)
|
return res.Format(time.TimeOnly)
|
||||||
}
|
}
|
||||||
return str
|
return str
|
||||||
@@ -403,3 +440,24 @@ func (dc *DataConverter) FormatData(dbColumnValue any, dataType dbi.DataType) st
|
|||||||
func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any {
|
func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any {
|
||||||
return dbColumnValue
|
return dbColumnValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (dc *DataConverter) WrapValue(dbColumnValue any, dataType dbi.DataType) string {
|
||||||
|
if dbColumnValue == nil {
|
||||||
|
return "NULL"
|
||||||
|
}
|
||||||
|
switch dataType {
|
||||||
|
case dbi.DataTypeNumber:
|
||||||
|
return fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
case dbi.DataTypeString:
|
||||||
|
val := fmt.Sprintf("%v", dbColumnValue)
|
||||||
|
// 转义单引号
|
||||||
|
val = strings.Replace(val, `'`, `''`, -1)
|
||||||
|
val = strings.Replace(val, `\''`, `\'`, -1)
|
||||||
|
// 转义换行符
|
||||||
|
val = strings.Replace(val, "\n", "\\n", -1)
|
||||||
|
return fmt.Sprintf("'%s'", val)
|
||||||
|
case dbi.DataTypeDate, dbi.DataTypeDateTime, dbi.DataTypeTime:
|
||||||
|
return fmt.Sprintf("'%s'", dc.FormatData(dbColumnValue, dataType))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("'%s'", dbColumnValue)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user