pg2dm-converter/README.md
dengqichen cd7788de95 init
2025-11-15 17:29:39 +08:00

17 KiB
Raw Blame History

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种类型)

  • 基础类型映射
    • int8BIGINT
    • int4INT
    • int2SMALLINT
    • boolBIT
    • numericDECIMAL
  • 浮点类型支持 🆕
    • float8DOUBLE (双精度浮点)
    • float4REAL (单精度浮点)
    • floatREAL
  • 字符类型完整支持 🆕
    • textVARCHAR(8000) (关键修复达梦TEXT是CLOB不能建索引)
    • bpcharCHAR (定长字符串)
    • varcharVARCHAR (变长字符串)
  • 时间戳类型完整支持 🆕
    • timestamp(6)TIMESTAMP (移除精度参数)
    • timestamptzTIMESTAMP (时间戳带时区)
    • 自动移除 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 falseDEFAULT 0, DEFAULT trueDEFAULT 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 COLUMNCOMMENT 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",
  ...
);

转换日志

每次转换会生成两个文件:

  1. SQL文件: output/filename_dm.sql - 转换后的SQL脚本
  2. 日志文件: 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
  }
};

注意事项

  1. 备份原始文件: 转换前请备份原始PostgreSQL SQL文件
  2. 检查转换结果: 转换后建议人工检查关键表和索引定义
  3. 测试执行: 在测试环境先执行转换后的SQL确认无误后再应用到生产环境
  4. 复杂索引: 对于警告的复杂索引,建议手动检查是否需要优化
  5. 函数索引: 达梦对函数索引表达式有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

错误处理策略

  1. 非破坏性转换: 无法识别的语法保持原样
  2. 警告机制: 潜在问题记录到日志但不中断转换
  3. 详细日志: JSON格式记录每一步操作
  4. 原文保留: 转换前备份建议

更新日志

  • v1.2.0 (2025-11-15) - 批量转换增强版 🆕

    • 批量转换功能 - 支持目录级批量处理
      • 无参数自动处理input目录
      • 支持指定目录批量转换
      • 详细进度显示和统计报告
    • 完整数据类型支持 - 新增多种类型映射
      • bpcharCHAR (定长字符串)
      • float8DOUBLE (双精度浮点)
      • float4REAL (单精度浮点)
      • timestamptzTIMESTAMP (时间戳带时区)
    • 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

技术支持

遇到问题请按以下步骤排查:

  1. 查看转换日志: output/*_conversion.log.json
  2. 检查警告信息: 控制台输出的WARN级别信息
  3. 验证SQL: 在达梦测试环境先执行部分语句
  4. 对比原始: 使用diff工具对比转换前后差异

贡献指南

欢迎提交Issue和Pull Request

重点改进方向:

  • 更多PostgreSQL特性支持
  • 性能优化
  • 错误处理增强
  • 测试用例完善