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

658 lines
17 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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](./EXECUTOR_README.md)
---
## 核心功能特性
本工具通过**12个转换步骤**解决PostgreSQL到达梦迁移过程中的**所有常见语法兼容性问题**
### 🚀 批量处理能力
-**智能批量转换** - 无参数自动处理input目录所有SQL文件
-**进度可视化** - 实时显示转换进度和统计信息
-**错误容错** - 单文件失败不影响其他文件转换
-**详细报告** - 批量转换完成后生成总体统计报告
### 1⃣ 模式与命名空间处理
-**移除`pg_catalog`模式前缀** - 达梦不识别PostgreSQL的系统模式
-**移除数据类型引号** - 达梦不需要给数据类型加引号
### 2⃣ 数据类型转换 (支持13种类型)
-**基础类型映射**
- `int8``BIGINT`
- `int4``INT`
- `int2``SMALLINT`
- `bool``BIT`
- `numeric``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语句提交一次可配置
- 失败自动回滚
-**可配置优化级别** - 根据场景选择优化策略
## 安装
```bash
cd pg2dm-converter
npm install
```
## 使用方法
### 1. 批量转换(推荐)⭐
```bash
# 无参数自动批量转换input目录下所有.sql文件
node converter.js
# 输出示例:
# 📁 批量转换目录: ./input
# ==================================================
# 找到 5 个SQL文件
#
# [1/5] 处理: schema1.sql
# --------------------------------------------------
# ✓ 转换完成: ./output/schema1_dm.sql
# ...
# ==================================================
# 📊 批量转换完成
# ==================================================
# ✓ 成功: 5 个文件
# 📂 输出目录: ./output
```
### 2. 单文件转换
```bash
# 基本用法
node converter.js input/your_schema.sql
# 输出: output/your_schema_dm.sql
```
### 3. 指定输出文件
```bash
node converter.js input/schema.sql output/custom_output.sql
```
### 4. 批量转换指定目录
```bash
# 转换指定目录下所有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)` 语法,达梦不支持
**解决方案**:
```sql
-- 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"` 格式,达梦不识别
**解决方案**:
```sql
-- PostgreSQL (转换前)
"col" "pg_catalog"."BIGINT" NOT NULL
-- 达梦 (转换后)
"col" BIGINT NOT NULL
```
### ❌ 问题3: COLLATE语法错误
**错误信息**: 语法分析出错
**原因**: PostgreSQL的COLLATE子句在达梦中不支持
**解决方案**:
```sql
-- PostgreSQL (转换前)
"name" varchar COLLATE "pg_catalog"."default" NOT NULL
-- 达梦 (转换后)
"name" varchar NOT NULL
```
### ❌ 问题4: TEXT类型建索引失败
**错误信息**: `-3207: 试图在BLOB/CLOB/用户自定义数据类型列上建索引`
**原因**: 达梦的TEXT是CLOB大对象类型不能建立索引
**解决方案**:
```sql
-- PostgreSQL (转换前)
"description" text
-- 达梦 (转换后)
"description" VARCHAR(8000)
```
### ❌ 问题5: 函数索引表达式太长
**错误信息**: `FUNCTION INDEX EXPRESSION TOO LONG`
**原因**: 达梦函数索引表达式限制816字符多个COALESCE超限
**解决方案**:
```sql
-- 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子句
**解决方案**:
```sql
-- PostgreSQL (转换前)
) PARTITION BY ();
-- 达梦 (转换后)
);
```
### ❌ 问题8: timestamp精度参数
**错误信息**: 语法分析出错
**原因**: 达梦不支持timestamp(6)的精度参数
**解决方案**:
```sql
-- PostgreSQL (转换前)
"created_at" timestamp(6)
-- 达梦 (转换后)
"created_at" TIMESTAMP
```
### ❌ 问题9: DECIMAL精度超出范围 🆕
**错误信息**: `-6121: 数据精度超出范围`
**原因**: PostgreSQL的DECIMAL最大精度1000位达梦只支持38位
**解决方案**:
```sql
-- PostgreSQL (转换前)
"coefficient" DECIMAL(50,0)
-- 达梦 (转换后) - 自动调整为38位
"coefficient" DECIMAL(38,0)
```
### ❌ 问题10: 带括号的类型有引号 🆕
**错误信息**: `-3719: 非法的基类名[VARCHAR(8000)]`
**原因**: text转换为VARCHAR(8000)后,类型引号移除逻辑无法处理带括号的类型
**解决方案**:
```sql
-- 转换中间结果 (错误)
"demand_order_no" "VARCHAR(8000)",
-- 达梦 (转换后) - 正确移除引号
"demand_order_no" VARCHAR(8000),
```
### ❌ 问题11: 索引注释不支持 🆕
**错误信息**: `-2007: 语法分析出错 [INDEX]附近出现错误`
**原因**: 达梦不支持 `COMMENT ON INDEX` 语法
**解决方案**:
```sql
-- 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 类型
**解决方案**:
```sql
-- 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):**
```sql
"id" int8 NOT NULL DEFAULT nextval('"schema".seq_name'::regclass)
```
**转换后 (达梦):**
```sql
"id" BIGINT IDENTITY(1, 1) NOT NULL
```
### 3. 索引语法简化
**转换前 (PostgreSQL):**
```sql
CREATE INDEX idx_name ON table_name USING btree (
"column1" COLLATE "pg_catalog"."default" "pg_catalog"."text_ops" ASC NULLS LAST
);
```
**转换后 (达梦):**
```sql
CREATE INDEX idx_name ON table_name (
"column1"
);
```
### 4. COALESCE函数索引处理
如果索引包含过多COALESCE函数默认>4个工具会
- 自动移除COALESCE包装保留原始列名
- 在日志中发出警告
- 在转换日志中记录详细信息
**示例:**
转换前:
```sql
CREATE UNIQUE INDEX idx ON table(
COALESCE("col1", '-999'),
COALESCE("col2", '-999'),
...
);
```
转换后:
```sql
CREATE UNIQUE INDEX idx ON table(
"col1",
"col2",
...
);
```
## 转换日志
每次转换会生成两个文件:
1. **SQL文件**: `output/filename_dm.sql` - 转换后的SQL脚本
2. **日志文件**: `output/filename_dm_conversion.log.json` - 详细转换日志
日志文件包含:
```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` 可以自定义转换规则:
```javascript
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. 序列转换
```javascript
// 匹配: "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清理
```javascript
// 匹配并移除: "pg_catalog".
const pattern = /"pg_catalog"\./gi;
```
#### 3. TEXT类型转换
```javascript
// 匹配: text (小写/大写)
// 替换为: VARCHAR(8000)
converted = converted.replace(/\s+text\s+/gi, ' VARCHAR(8000) ');
```
#### 4. COALESCE索引简化
```javascript
// 匹配: 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目录
- 支持指定目录批量转换
- 详细进度显示和统计报告
-**完整数据类型支持** - 新增多种类型映射
- `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
## 技术支持
遇到问题请按以下步骤排查:
1. **查看转换日志**: `output/*_conversion.log.json`
2. **检查警告信息**: 控制台输出的WARN级别信息
3. **验证SQL**: 在达梦测试环境先执行部分语句
4. **对比原始**: 使用diff工具对比转换前后差异
## 贡献指南
欢迎提交Issue和Pull Request
重点改进方向:
- 更多PostgreSQL特性支持
- 性能优化
- 错误处理增强
- 测试用例完善