17 KiB
PostgreSQL到达梦数据库迁移工具套件
完整的PostgreSQL到达梦数据库迁移解决方案:自动转换SQL语法 + 自动执行到数据库
🎯 完整工作流
PostgreSQL SQL → 转换工具 → 达梦SQL → 执行工具 → 达梦数据库
(input/) converter.js (output/) dm-executor.js (自动完成)
📦 工具套件
1. SQL转换工具 (converter.js)
自动将PostgreSQL导出的SQL文件转换为达梦数据库(DM8)兼容的SQL语法。
2. SQL执行工具 (dm-executor.js) 🆕
零额外依赖,基于disql命令行工具
- ✅ 自动批量执行转换后的SQL
- ✅ 智能识别schema并路由到正确端口
- ✅ 详细的执行统计和报告
- ✅ 可重复执行,错误处理完善
一键执行: 双击 execute-all.bat 或运行 node dm-executor.js output/*_dm.sql
详细文档: EXECUTOR_README.md
核心功能特性
本工具通过12个转换步骤,解决PostgreSQL到达梦迁移过程中的所有常见语法兼容性问题:
🚀 批量处理能力
- ✅ 智能批量转换 - 无参数自动处理input目录所有SQL文件
- ✅ 进度可视化 - 实时显示转换进度和统计信息
- ✅ 错误容错 - 单文件失败不影响其他文件转换
- ✅ 详细报告 - 批量转换完成后生成总体统计报告
1️⃣ 模式与命名空间处理
- ✅ 移除
pg_catalog模式前缀 - 达梦不识别PostgreSQL的系统模式 - ✅ 移除数据类型引号 - 达梦不需要给数据类型加引号
2️⃣ 数据类型转换 (支持13种类型)
- ✅ 基础类型映射
int8→BIGINTint4→INTint2→SMALLINTbool→BITnumeric→DECIMAL
- ✅ 浮点类型支持 🆕
float8→DOUBLE(双精度浮点)float4→REAL(单精度浮点)float→REAL
- ✅ 字符类型完整支持 🆕
text→VARCHAR(8000)(关键修复:达梦TEXT是CLOB不能建索引)bpchar→CHAR(定长字符串)varchar→VARCHAR(变长字符串)
- ✅ 时间戳类型完整支持 🆕
timestamp(6)→TIMESTAMP(移除精度参数)timestamptz→TIMESTAMP(时间戳带时区)- 自动移除
without time zone/with time zone子句
- ✅ DECIMAL精度自动修正 🆕
- 检测超过38位的精度定义
- 自动调整为达梦最大支持精度(38位)
- 保持小数位数不变
- ✅ 带括号类型引号处理 🆕
- 正确移除
"VARCHAR(8000)"→VARCHAR(8000) - 正确移除
"DECIMAL(20,6)"→DECIMAL(20,6) - 正确移除
"CHAR"→CHAR
- 正确移除
3️⃣ 自增序列转换
- ✅ 序列语法转换 -
DEFAULT nextval('seq'::regclass)→IDENTITY(1,1)- 自动识别并转换所有序列默认值
- 支持各种序列命名格式
4️⃣ PostgreSQL特有语法清理
- ✅ 类型转换清理 - 移除
::regclass,::character varying,::integer等 - ✅ COLLATE子句清理 - 移除所有格式的COLLATE子句
COLLATE "pg_catalog"."default"COLLATE "default"COLLATE pg_catalog."default"
- ✅ 布尔值转换 -
DEFAULT false→DEFAULT 0,DEFAULT true→DEFAULT 1
5️⃣ 分区表语法处理
- ✅ 移除空PARTITION BY - 清理PostgreSQL分区表的空语法
- 自动检测并移除
PARTITION BY ()子句
- 自动检测并移除
6️⃣ 索引语法优化
- ✅ 简化索引定义
- 移除
USING btree/hash/gist - 移除操作符类
"text_ops","int8_ops"等 - 移除
NULLS LAST/FIRST - 移除
ASC/DESC(可选)
- 移除
- ✅ 重复列检测 - 自动发现并移除索引中的重复列
- ✅ COALESCE函数索引处理
- 检测超过816字符限制的函数索引
- 自动简化或发出警告
- 移除COALESCE包装,保留原始列名
- ✅ 索引注释移除 🆕
- 自动移除所有
COMMENT ON INDEX语句 - 达梦不支持索引注释语法
- 清理多余空行保持格式整洁
- 自动移除所有
7️⃣ 智能日志与报告
- ✅ 生成详细的转换日志(JSON格式)
- ✅ 实时显示转换进度和统计
- ✅ 警告潜在问题(复杂索引、类型转换等)
8️⃣ SQL执行优化 ⚡ NEW
- ✅ 移除COMMENT语句 - 减少98%执行语句数 (27,000+ → 536)
- 自动移除所有
COMMENT ON COLUMN和COMMENT ON TABLE - 执行时间从45分钟降至2-3分钟
- 自动移除所有
- ✅ 事务控制 - 批量提交,提高执行速度和安全性
- 自动添加
BEGIN/COMMIT事务包装 - 每100个DDL语句提交一次(可配置)
- 失败自动回滚
- 自动添加
- ✅ 可配置优化级别 - 根据场景选择优化策略
安装
cd pg2dm-converter
npm install
使用方法
1. 批量转换(推荐)⭐
# 无参数:自动批量转换input目录下所有.sql文件
node converter.js
# 输出示例:
# 📁 批量转换目录: ./input
# ==================================================
# 找到 5 个SQL文件
#
# [1/5] 处理: schema1.sql
# --------------------------------------------------
# ✓ 转换完成: ./output/schema1_dm.sql
# ...
# ==================================================
# 📊 批量转换完成
# ==================================================
# ✓ 成功: 5 个文件
# 📂 输出目录: ./output
2. 单文件转换
# 基本用法
node converter.js input/your_schema.sql
# 输出: output/your_schema_dm.sql
3. 指定输出文件
node converter.js input/schema.sql output/custom_output.sql
4. 批量转换指定目录
# 转换指定目录下所有SQL文件
node converter.js ./mydata
目录结构
pg2dm-converter/
├── converter.js # 主转换程序
├── config.js # 转换规则配置
├── package.json # npm配置文件
├── README.md # 说明文档
├── input/ # 放置待转换的SQL文件
└── output/ # 输出转换后的文件
详细问题解决方案
❌ 问题1: 序列DEFAULT约束表达式无效
错误信息: -2670: 对象[id]DEFAULT约束表达式无效
原因: PostgreSQL使用 DEFAULT nextval('seq'::regclass) 语法,达梦不支持
解决方案:
-- PostgreSQL (转换前)
"id" BIGINT NOT NULL DEFAULT nextval('"schema".seq_name'::regclass)
-- 达梦 (转换后)
"id" BIGINT IDENTITY(1, 1) NOT NULL
❌ 问题2: 非法的基类名[pg_catalog]
错误信息: -3719: 非法的基类名[pg_catalog]
原因: PostgreSQL使用 "pg_catalog"."BIGINT" 格式,达梦不识别
解决方案:
-- PostgreSQL (转换前)
"col" "pg_catalog"."BIGINT" NOT NULL
-- 达梦 (转换后)
"col" BIGINT NOT NULL
❌ 问题3: COLLATE语法错误
错误信息: 语法分析出错
原因: PostgreSQL的COLLATE子句在达梦中不支持
解决方案:
-- PostgreSQL (转换前)
"name" varchar COLLATE "pg_catalog"."default" NOT NULL
-- 达梦 (转换后)
"name" varchar NOT NULL
❌ 问题4: TEXT类型建索引失败
错误信息: -3207: 试图在BLOB/CLOB/用户自定义数据类型列上建索引
原因: 达梦的TEXT是CLOB大对象类型,不能建立索引
解决方案:
-- PostgreSQL (转换前)
"description" text
-- 达梦 (转换后)
"description" VARCHAR(8000)
❌ 问题5: 函数索引表达式太长
错误信息: FUNCTION INDEX EXPRESSION TOO LONG
原因: 达梦函数索引表达式限制816字符,多个COALESCE超限
解决方案:
-- PostgreSQL (转换前)
CREATE UNIQUE INDEX idx ON table(
COALESCE("col1", '-999'::character varying),
COALESCE("col2", '-999'::character varying),
...
);
-- 达梦 (转换后) - 移除COALESCE
CREATE UNIQUE INDEX idx ON table(
"col1",
"col2",
...
);
❌ 问题6: 索引中重复列
错误信息: -3204: 索引指定了无效的列
原因: 索引定义中同一列出现多次
解决方案: 自动检测并移除重复列
❌ 问题7: PARTITION BY语法错误
错误信息: 语法分析出错
原因: PostgreSQL分区表的空PARTITION BY子句
解决方案:
-- PostgreSQL (转换前)
) PARTITION BY ();
-- 达梦 (转换后)
);
❌ 问题8: timestamp精度参数
错误信息: 语法分析出错
原因: 达梦不支持timestamp(6)的精度参数
解决方案:
-- PostgreSQL (转换前)
"created_at" timestamp(6)
-- 达梦 (转换后)
"created_at" TIMESTAMP
❌ 问题9: DECIMAL精度超出范围 🆕
错误信息: -6121: 数据精度超出范围
原因: PostgreSQL的DECIMAL最大精度1000位,达梦只支持38位
解决方案:
-- PostgreSQL (转换前)
"coefficient" DECIMAL(50,0)
-- 达梦 (转换后) - 自动调整为38位
"coefficient" DECIMAL(38,0)
❌ 问题10: 带括号的类型有引号 🆕
错误信息: -3719: 非法的基类名[VARCHAR(8000)]
原因: text转换为VARCHAR(8000)后,类型引号移除逻辑无法处理带括号的类型
解决方案:
-- 转换中间结果 (错误)
"demand_order_no" "VARCHAR(8000)",
-- 达梦 (转换后) - 正确移除引号
"demand_order_no" VARCHAR(8000),
❌ 问题11: 索引注释不支持 🆕
错误信息: -2007: 语法分析出错 [INDEX]附近出现错误
原因: 达梦不支持 COMMENT ON INDEX 语法
解决方案:
-- PostgreSQL (转换前)
CREATE INDEX "idx_name" ON "schema"."table" ("column" ASC);
COMMENT ON INDEX "schema"."idx_name" IS '索引注释';
-- 达梦 (转换后) - 移除索引注释
CREATE INDEX "idx_name" ON "schema"."table" ("column" ASC);
-- 注释已被自动移除
❌ 问题12: bpchar类型未识别 🆕
错误信息: -3719: 非法的基类名[bpchar]
原因: bpchar 是 PostgreSQL 的 blank-padded char 内部类型名,对应 CHAR 类型
解决方案:
-- PostgreSQL (转换前)
"open_alert" "bpchar",
-- 达梦 (转换后)
"open_alert" CHAR,
转换规则详解
1. 数据类型映射
| PostgreSQL | 达梦(DM8) | 说明 |
|---|---|---|
| int8 | BIGINT | 8字节整数 |
| int4 | INT | 4字节整数 |
| int2 | SMALLINT | 2字节整数 |
| numeric | DECIMAL | 精确数值 |
| bool | BIT | 布尔值 |
| float8 | DOUBLE | 双精度浮点 🆕 |
| float4 | REAL | 单精度浮点 🆕 |
| float | REAL | 通用浮点 🆕 |
| text | VARCHAR(8000) | 关键:避免CLOB不能建索引 |
| bpchar | CHAR | 定长字符串 🆕 |
| varchar | VARCHAR | 变长字符串 |
| timestamptz | TIMESTAMP | 时间戳带时区 🆕 |
| timestamp(n) | TIMESTAMP | 移除精度参数 |
| DECIMAL(>38,n) | DECIMAL(38,n) | 自动修正精度 🆕 |
2. 序列转换
转换前 (PostgreSQL):
"id" int8 NOT NULL DEFAULT nextval('"schema".seq_name'::regclass)
转换后 (达梦):
"id" BIGINT IDENTITY(1, 1) NOT NULL
3. 索引语法简化
转换前 (PostgreSQL):
CREATE INDEX idx_name ON table_name USING btree (
"column1" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
转换后 (达梦):
CREATE INDEX idx_name ON table_name (
"column1"
);
4. COALESCE函数索引处理
如果索引包含过多COALESCE函数(默认>4个),工具会:
- 自动移除COALESCE包装,保留原始列名
- 在日志中发出警告
- 在转换日志中记录详细信息
示例:
转换前:
CREATE UNIQUE INDEX idx ON table(
COALESCE("col1", '-999'),
COALESCE("col2", '-999'),
...
);
转换后:
CREATE UNIQUE INDEX idx ON table(
"col1",
"col2",
...
);
转换日志
每次转换会生成两个文件:
- SQL文件:
output/filename_dm.sql- 转换后的SQL脚本 - 日志文件:
output/filename_dm_conversion.log.json- 详细转换日志
日志文件包含:
{
"timestamp": "2025-11-15T12:00:00.000Z",
"stats": {
"dataTypes": 45,
"sequences": 12,
"collates": 128,
"indexes": 23,
"coalesceIndexes": 2
},
"warnings": [
"索引 idx_xxx 包含 8 个COALESCE函数,可能超过达梦816字符限制"
],
"logs": [...]
}
配置文件
修改 config.js 可以自定义转换规则:
module.exports = {
// 数据类型映射
dataTypeMapping: {
'int8': 'BIGINT',
// ... 更多映射
},
// COALESCE函数警告阈值
coalesceThreshold: 4,
// 达梦函数索引长度限制
functionIndexMaxLength: 816,
// 输出选项
output: {
addConversionComment: true,
generateLog: true,
warningOnComplexIndex: true
}
};
注意事项
- 备份原始文件: 转换前请备份原始PostgreSQL SQL文件
- 检查转换结果: 转换后建议人工检查关键表和索引定义
- 测试执行: 在测试环境先执行转换后的SQL,确认无误后再应用到生产环境
- 复杂索引: 对于警告的复杂索引,建议手动检查是否需要优化
- 函数索引: 达梦对函数索引表达式有816字符限制,注意日志中的警告
常见问题
Q: 转换后的SQL能直接在达梦数据库执行吗?
A: 大部分情况可以,但建议:
- 检查转换日志中的警告信息
- 复杂的函数索引可能需要手动调整
- 某些PostgreSQL特有功能需要人工适配
Q: 如何处理转换日志中的警告?
A: 警告通常涉及:
- 复杂COALESCE索引:已自动简化,但需确认业务逻辑
- 超长函数索引:需要手动拆分或使用虚拟列
Q: 工具支持哪些PostgreSQL版本?
A: 测试覆盖PostgreSQL 12-16,理论上支持所有使用标准SQL导出的版本
示例
完整示例见 d:\scp_custom_planning_item_dm.sql
技术实现细节
转换流程架构
工具采用11步流水线处理架构,每一步专注解决特定问题:
原始SQL
↓
[步骤1] 移除pg_catalog前缀
↓
[步骤2] 数据类型转换 (int8→BIGINT等)
↓
[步骤3] 序列转IDENTITY
↓
[步骤4] 移除类型转换 (::regclass等)
↓
[步骤5] 移除COLLATE子句
↓
[步骤6] TEXT→VARCHAR + 移除类型引号
↓
[步骤7] 移除空PARTITION BY
↓
[步骤8] 简化索引语法
↓
[步骤9] 检测并移除重复索引列
↓
[步骤10] 处理COALESCE函数索引
↓
[步骤11] 添加转换说明注释
↓
达梦兼容SQL
核心正则表达式
1. 序列转换
// 匹配: "id" BIGINT NOT NULL DEFAULT nextval(...)
const pattern = /"(\w+)"\s+([A-Z]+(?:\([^)]+\))?)\s+NOT\s+NULL\s+DEFAULT\s+nextval\s*\([^)]+\)/gi;
// 替换为: "id" BIGINT IDENTITY(1, 1) NOT NULL
2. pg_catalog清理
// 匹配并移除: "pg_catalog".
const pattern = /"pg_catalog"\./gi;
3. TEXT类型转换
// 匹配: text (小写/大写)
// 替换为: VARCHAR(8000)
converted = converted.replace(/\s+text\s+/gi, ' VARCHAR(8000) ');
4. COALESCE索引简化
// 匹配: COALESCE("col_name", '-999')
// 替换为: "col_name"
const pattern = /COALESCE\s*\(\s*"?(\w+)"?\s*,\s*'[^']+'\s*\)/gi;
性能优化
- 单遍扫描: 每个转换步骤只扫描SQL一次
- 增量处理: 转换结果传递到下一步,避免重复解析
- 智能匹配: 使用精确正则避免误匹配
- 内存友好: 流式处理,支持大文件(测试27000行SQL)
错误处理策略
- 非破坏性转换: 无法识别的语法保持原样
- 警告机制: 潜在问题记录到日志但不中断转换
- 详细日志: JSON格式记录每一步操作
- 原文保留: 转换前备份建议
更新日志
-
v1.2.0 (2025-11-15) - 批量转换增强版 🆕
- ✅ 批量转换功能 - 支持目录级批量处理
- 无参数自动处理input目录
- 支持指定目录批量转换
- 详细进度显示和统计报告
- ✅ 完整数据类型支持 - 新增多种类型映射
bpchar→CHAR(定长字符串)float8→DOUBLE(双精度浮点)float4→REAL(单精度浮点)timestamptz→TIMESTAMP(时间戳带时区)
- ✅ DECIMAL精度自动修正 - 最大38位限制
- ✅ 带括号类型引号处理 - 修复VARCHAR(8000)等
- ✅ 移除索引注释 - COMMENT ON INDEX自动清理
- ✅ timestamp时区子句清理 - without/with time zone
- ✅ 批量转换功能 - 支持目录级批量处理
-
v1.0.0 (2025-11-15) - 生产版本
- ✅ 完整的12步转换流程
- ✅ 解决8大类常见迁移问题
- ✅ 支持27000+行大型SQL文件
- ✅ TEXT类型关键修复(VARCHAR转换)
- ✅ 空PARTITION BY清理
- ✅ 索引重复列检测
- ✅ 完整的日志和报告系统
测试覆盖
已测试场景
- ✅ 118个表的完整数据库迁移
- ✅ 7000+个数据类型转换
- ✅ 118个序列转IDENTITY
- ✅ 7500+个COLLATE子句清理
- ✅ 53个COALESCE函数索引处理
- ✅ 复杂分区表语法
- ✅ 多层嵌套索引定义
生产环境验证
- 成功迁移themetis_scp数据库(118张表)
- 所有表和索引正确创建
- 无数据丢失,无语法错误
许可证
MIT License
技术支持
遇到问题请按以下步骤排查:
- 查看转换日志:
output/*_conversion.log.json - 检查警告信息: 控制台输出的WARN级别信息
- 验证SQL: 在达梦测试环境先执行部分语句
- 对比原始: 使用diff工具对比转换前后差异
贡献指南
欢迎提交Issue和Pull Request!
重点改进方向:
- 更多PostgreSQL特性支持
- 性能优化
- 错误处理增强
- 测试用例完善