mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20: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")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		insertSql := "INSERT INTO %s VALUES (%s);\n"
 | 
					 | 
				
			||||||
		dbConn.WalkTableRows(ctx, table, func(record map[string]any, columns []*dbi.QueryColumn) error {
 | 
					 | 
				
			||||||
			var values []string
 | 
					 | 
				
			||||||
			writer.TryFlush()
 | 
					 | 
				
			||||||
	for _, column := range columns {
 | 
						for _, column := range columns {
 | 
				
			||||||
				value := record[column.Name]
 | 
							columnMap[column.TableName] = append(columnMap[column.TableName], column)
 | 
				
			||||||
				if value == nil {
 | 
					 | 
				
			||||||
					values = append(values, "NULL")
 | 
					 | 
				
			||||||
					continue
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
				strValue, ok := value.(string)
 | 
					
 | 
				
			||||||
				if ok {
 | 
						// 按表名排序
 | 
				
			||||||
					strValue = dbMeta.QuoteLiteral(strValue)
 | 
						sort.Strings(tables)
 | 
				
			||||||
					values = append(values, strValue)
 | 
					
 | 
				
			||||||
				} else {
 | 
						quoteSchema := dbMeta.QuoteIdentifier(dbConn.Info.CurrentSchema())
 | 
				
			||||||
					values = append(values, anyx.ToString(value))
 | 
					
 | 
				
			||||||
 | 
						// 遍历获取每个表的信息
 | 
				
			||||||
 | 
						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)))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							tabInfo := dbi.Table{
 | 
				
			||||||
 | 
								TableName:    tableName,
 | 
				
			||||||
 | 
								TableComment: tbs[0].TableComment,
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 生成表结构信息
 | 
				
			||||||
 | 
							if needStruct {
 | 
				
			||||||
 | 
								writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", tableName))
 | 
				
			||||||
 | 
								tbDdlArr := dbMeta.GenerateTableDDL(columnMap[tableName], tabInfo, true)
 | 
				
			||||||
 | 
								for _, ddl := range tbDdlArr {
 | 
				
			||||||
 | 
									writer.WriteString(ddl + ";\n")
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
			writer.WriteString(fmt.Sprintf(insertSql, quotedTable, strings.Join(values, ", ")))
 | 
					
 | 
				
			||||||
 | 
							// 生成insert sql,数据在索引前,加速insert
 | 
				
			||||||
 | 
							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
 | 
									return nil
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
		writer.WriteString("COMMIT;\n")
 | 
					
 | 
				
			||||||
 | 
								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,12 +55,12 @@ 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,14 +35,14 @@ 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
 | 
				
			||||||
@@ -50,14 +50,13 @@ 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
 | 
				
			||||||
 | 
						if len(sqlArr) > 0 {
 | 
				
			||||||
 | 
							for _, sqlStr := range sqlArr {
 | 
				
			||||||
 | 
								_, err := dd.dc.Exec(sqlStr)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
				return err
 | 
									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,6 +119,14 @@ func (md *MssqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
 | 
				
			|||||||
			NumScale:      cast.ToInt(re["NUM_SCALE"]),
 | 
								NumScale:      cast.ToInt(re["NUM_SCALE"]),
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							md.FixColumn(&column)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							columns = append(columns, column)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return columns, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (md *MssqlMetaData) FixColumn(column *dbi.Column) {
 | 
				
			||||||
	dataType := strings.ToLower(string(column.DataType))
 | 
						dataType := strings.ToLower(string(column.DataType))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if collx.ArrayAnyMatches([]string{"date", "time"}, dataType) {
 | 
						if collx.ArrayAnyMatches([]string{"date", "time"}, dataType) {
 | 
				
			||||||
@@ -135,10 +143,6 @@ func (md *MssqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
 | 
				
			|||||||
		// 如果是nvarchar,可视长度减半
 | 
							// 如果是nvarchar,可视长度减半
 | 
				
			||||||
		column.CharMaxLength = column.CharMaxLength / 2
 | 
							column.CharMaxLength = column.CharMaxLength / 2
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
		columns = append(columns, column)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return columns, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
 | 
					// 获取表主键字段名,不存在主键标识则默认第一个字段
 | 
				
			||||||
@@ -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