---
slug: "sql-splitter-x-7"
source_type: "clawhub"
source_url: "https://clawhub.ai/skills/sql-splitter"
repo: ""
source_file: "description"
---
---

name: sql-splitter

description: 拆分 SQL 文件为独立文件（存储过程、函数、视图、触发器、表结构、索引、约束），自动分析依赖并生成合并脚本

---



# SQL 文件拆分工具 v3.6.2



将包含多个 SQL 对象的单一文件或目录拆分为独立的 .sql 文件，

并自动分析对象间依赖关系，生成按依赖排序的合并脚本。



## v3.4.5 修复 — dbo两段式替换 + DATETIME2排序 + 方括号替换架构优化 + SELECT INTO转换 + DDL分号



- **v3.5.4 新增: 过程体内所有语句(DML+变量赋值)补分号** — 新增`_ensure_statement_semicolons()`方法(Step 8.6)，处理DML和变量赋值的分号补全，同时正确处理跨行INSERT INTO

- **v3.5.3 行为变更: 非类型名方括号→加双引号** — `[aa]`→`"aa"`(v3.5.3起加双引号; v3.5.2及以前去[]不加双引号→`aa`)，类型名`[int]`→`int`仍只去[]不加双引号。修改位置：`_convert_bracket_identifiers()`新增`_replace_bracket`回调函数，用TYPE_MAPPINGS键集合区分类型名和普通标识符



- **dbo替换规则(v3.5.6最终版)** — 三段式和两段式区别处理：

  - **三段式**（已有其他schema名，如`HRBI_Stage.[dbo].[xxx]`）: dbo是SQL Server默认schema，直接删除

  - **两段式**（只有dbo，如`[dbo].[xxx]`或`dbo.xxx`）: dbo替换为schema_prefix

  - 关键区分：输入中若有其他schema名存在，dbo是冗余层→删除；若只有dbo，dbo是唯一schema标识→替换为prefix

  - **正则替换顺序（不可颠倒）**: 1)双点号`(\\w+)\\.\\.(\\w+)`→`\\1.\\2` 2)三段式引号`"\\w+"\\.dbo\\.\"(\\w+)\"`→`"$1"` 3)三段式裸名`(\\w+)\\.dbo\\.(\\w+)`→`"$1\".\"$2\"` 4)三段式混合`"\\w+\"\\.\\w+\\.(\\w+)`→保留 5)两段式引号`"dbo\"\\.\"(\\w+)\"`→`prefix.\"$1\"` 6)两段式裸名`dbo\\.(\\w+)`→`prefix.$1`

  - 实现位置：`_replace_dbo_prefix()`

  - ⚠️ 改完dbo规则后必须跑全量312过程验证

  - 详见 [dbo演进史](references/dm-converter-v345-fixes.md)

- **DATETIME2→TIMESTAMP2而非TIMESTAMP(BUG)** — TYPE_MAPPINGS的key构建正则alternation时无排序，`DATETIME`排在`DATETIME2`前面抢先匹配，`DATETIME2`被拆成`DATETIME`+`2`变成`TIMESTAMP2`。修复：`sorted(keys, key=len, reverse=True)`按长度降序排列，`DATETIME2`(9字符)排在`DATETIME`(8字符)前面。影响范围：全局`_TYPE_NAMES_PATTERN`、`_FULL_TYPE_NAMES_PATTERN`、以及`_post_convert_table_types`和`_post_convert_generic_types`中的两个`_bare_type_pattern`

- **方括号替换在token还原后执行导致注释内容被误改(架构BUG)** — `_post_convert_table_types`/`_post_convert_generic_types`中的`\\[([^\\]]+)\\]`在Step 6.7(token还原后)执行，注释已还原为原始文本，注释中含方括号(如`---一次性解决率 是[处理人响应次数`)会被误匹配截断。修复：把方括号替换从Step 6.7移到Step 4`_convert_bracket_identifiers()`中执行——此时注释和字符串已被tokenize保护，方括号只出现在真实SQL代码中，不会被注释内容干扰

- **SELECT INTO #临时表→CTAS创建GTT(新规则)** — 达梦不支持`SELECT ... INTO #新表 FROM ...`建表语法。转换规则：临时表(`INTO #xxx`/`INTO tmp_xxx`)→`CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT ... FROM ...`(CTAS方式)；普通表暂不自动转换(与达梦变量赋值语法`SELECT expr INTO var FROM`形式相同，无法区分)。修改位置：`_convert_temp_tables()`新增SELECT INTO处理逻辑

- **过程体内DDL语句结尾加分号(新规则)** — 达梦存储过程体内每条DDL(CREATE/ALTER/DROP TABLE/INDEX等)必须以`;`结尾。新增`_ensure_ddl_semicolons()`方法(Step 8.5)，用状态机扫描过程体：单行DDL如`DROP TABLE xxx`直接补`;`，跨行DDL如`CREATE GLOBAL TEMPORARY TABLE "tmp_xxx"\n(id INT)\n`在`)`行补`;`，CTAS跨行子查询在遇到下一个语句开头时给上一行补`;`，同时处理`ON PRIMARY`文件组语法去掉。312个存储过程中DDL块缺分号数从447降为0

- **过程体内所有语句(DML+变量赋值)结尾加分号(v3.5.4起)** — 新增`_ensure_statement_semicolons()`方法(Step 8.6)。达梦存储过程体内DML(INSERT/DELETE/UPDATE/SELECT)和变量赋值(`:=`)也必须以分号结尾。关键：跨行INSERT INTO(table_name后无括号，列定义在下一行)不加分号；单行DELETE FROM...WHERE...补分号；v_xxx := expr补分号；跨行赋值((/+,/结尾)不补分号。312个存储过程全部验证通过。

- **过程体内DML语句和变量赋值结尾加分号(v3.5.6新增)** — 达梦存储过程体内每条DML(INSERT/DELETE/UPDATE/SELECT)和变量赋值(`v_xxx := expr`)必须以`;`结尾。新增`_ensure_statement_semicolons()`方法(Step 8.6)，逐行扫描过程体：单行DML如`delete FROM xxx WHERE y=1`直接补`;`，跨行INSERT INTO(table_name\n(columns)\nSELECT ...)不加分号，列定义结束后自动补`;`；变量赋值`v_xxx := expr`直接补`;`，跨行赋值(`v_xxx := ('`或`v_xxx := expr +`)不加分号。312个存储过程中DML/变量赋值缺分号数从数百降为0

- **schema_prefix传参链路断裂** — `split_sql_v21.py`的`_convert_split_output()`函数签名有schema_prefix参数，但调用处(第886行)没传，convert调用(第977行)也没传，导致dbo替换永远不生效。修复：调用链全程传递schema_prefix

- **新增CLI参数`--schema-prefix`和`--dm`** — `--schema-prefix`手动指定dbo替换前缀；`--dm`是`--convert-to dm`快捷方式。默认从输入文件名自动提取schema_prefix

- **6条用户规则验证通过**：1) []替换规则：`[aa]`→`"aa"`(去[]加双引号)、`[hrbi].[xxx]`→`"hrbi"."xxx"`(schema/表名加双引号)、`[int]`→`int`/`[nvarchar]`→`nvarchar`(类型名去[]不加双引号，后续做类型映射) 2) dbo替换规则：三段式`[HRBI].[dbo].[xxx]`→`"HRBI"."xxx"`(删dbo)/两段式`[dbo].[xxx]`→`"hrbi_stage"."xxx"`(替换为prefix)/裸名`dbo.xxx`→`hrbi_stage.xxx`(替换为prefix) 3) VARCHAR(n)加CHAR定义 4) DATETIME/DATETIME2→TIMESTAMP 5) NCHAR→CHAR 6) SELECT INTO #临时表→CTAS创建GTT

- **7条用户规则验证通过**：1) []替换规则：`[aa]`→`"aa"`(去[]加双引号)、`[hrbi].[xxx]`→`"hrbi"."xxx"`(schema/表名加双引号)、`[int]`→`int`/`[nvarchar]`→`nvarchar`(类型名去[]不加双引号，后续做类型映射) 2) dbo替换规则：三段式`[HRBI].[dbo].[xxx]`→`"HRBI"."xxx"`(删dbo)/两段式`[dbo].[xxx]`→`"hrbi_stage"."xxx"`(替换为prefix)/裸名`dbo.xxx`→`hrbi_stage.xxx`(替换为prefix) 3) VARCHAR(n)加CHAR定义 4) DATETIME/DATETIME2→TIMESTAMP 5) NCHAR→CHAR 6) SELECT INTO #临时表→CTAS创建GTT 7) 过程体内DDL语句结尾加分号

- **312个存储过程转换全部成功**



当多个模式有公共前缀时（如`DATETIME`和`DATETIME2`），正则alternation`|`从左到右匹配，公共前缀排在前面会抢先匹配，留下后缀字符。**必须按长度降序排列alternation中的模式**。同理适用于`SMALLDATETIME`(13字符)>`DATETIMEOFFSET`(15字符)>`DATETIME2`(9字符)>`DATETIME`(8字符)等所有有前缀关系的类型名。这不仅是dm_converter的问题——任何用`|`连接多个可变长度keyword的正则都需注意此陷阱。



### ⚠️ 方括号替换必须在token保护下执行



`_convert_bracket_identifiers()`（Step 4，token化后/还原前）是方括号替换的唯一正确位置。此时注释和字符串已被替换为`__TOKEN_N__`占位符，方括号只出现在真实SQL代码中。如果在token还原后（Step 6.7）做方括号替换，注释中的`[`和`]`也会被误处理——即使`[^\]\n]+`限制不跨行，注释内不含换行时仍会跨标识符贪婪匹配。



### ⚠️ schema_prefix必须贯穿调用链



`split_sql_file()` → `_convert_split_output()` → `convert_sqlserver_to_dm_with_result()` → `DMConverter.convert()` → `_replace_dbo_prefix()`。任何一环漏传schema_prefix，dbo替换就不生效。新增`schema_prefix`参数到`split_sql_file()`签名，CLI默认从输入文件名提取。



## v3.4.0 新功能 — TRUNCATE→DELETE FROM + TABLE/VIEW结尾加分号 + 所有方言拆分加分号



- **TRUNCATE TABLE → DELETE FROM** — 达梦不支持TRUNCATE在存储过程内，自动将 `TRUNCATE TABLE xxx` 转换为 `DELETE FROM xxx`

- **TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE结尾加分号** — 拆分后的表、视图等DDL对象结尾自动加 `;`，确保达梦可直接执行（新增 `_add_ending_semicolon` 方法）

- **所有方言拆分后均加分号** — 之前Oracle/DM方言拆分后不加分号（用`/`代替），现在统一加分号（split_sql_v21.py已去掉Oracle/DM特殊逻辑）

- **53个单元测试全部通过**（新增9个：4个TRUNCATE转换+5个结尾分号）

- **⚠️ TRUNCATE→DELETE语义差异**：TRUNCATE是DDL（不可回滚、重置自增），DELETE是DML（可回滚、不重置自增）。自动转换后行为不完全等价，但这是达梦过程体内的唯一可行方案。如果需要重置自增列，需在DELETE后手动调用序列重置。



## v3.3.0 新功能 — PROCEDURE双重引号修复 + IDENTITY位置修正 + 临时表正则修复 + token碰撞修复



- **存储过程PROCEDURE双重引号bug修复** — 之前三个正则顺序执行，第一个替换`PROCEDURE sp_test(...)` 后输出 `"sp_test"`，第三个又匹配到已替换的结果再包引号变成 `""sp_test""` 。修复：合并为一个正则+分支回调，确保每个存储过程声明只被匹配和替换一次

- **IDENTITY自增列位置修正** — 之前 `IDENTITY("id",1,1)` 被插在表定义的 `)` 前面，达梦语法要求紧跟 `)` 后面。修复：优先匹配独占一行的 `)` ，也兼容单行写法 `(id INT IDENTITY(1,1) NOT NULL)`

- **#临时表正则修复** — 之前 `[^]]*` 匹配到 `NVARCHAR(100)` 的 `)` 就截断，导致临时表列定义不完整。修复：改用贪婪 `(.+)` + `re.DOTALL` 匹配到最后一个 `)` 。同时修复 `##` 全局临时表先被替换为 `#tmp_` 的bug

- **token_map碰撞修复** — Step3重新tokenize时counter从0开始，`"v_users"` 新占位符覆盖了 `'N/A'` 原占位符，导致字符串被替换为标识符。修复：新增 `start_counter` 参数，Step3从原最大key+1开始编号

- **44个单元测试全部通过**



## v3.2.x 功能 — PROCEDURE用AS + VARCHAR(n CHAR)对PROC生效 + CAST中nvarchar映射



- **存储过程PROCEDURE用AS而非IS(v3.2.3)** — 达梦存储过程声明用`AS`，函数用`IS`，之前PROCEDURE错误用了Oracle风格的`IS`。**关键区分：PROCEDURE→AS，FUNCTION→IS**

- **存储过程VARCHAR(n)加CHAR语义(v3.2.2)** — DECLARE变量和参数中的`VARCHAR(n)` → `VARCHAR(n CHAR)`，与TABLE转换一致

- **CAST中nvarchar→VARCHAR(n CHAR)(v3.2.2)** — `cast(x as nvarchar(50))` → `CAST(x AS VARCHAR(50 CHAR))`，之前nvarchar在CAST中未被映射

- **_post_convert_generic_types增强(v3.2.2)** — 新增`_bare_type_pattern`映射裸类型名（如CAST中nvarchar），之前只映射方括号包裹的类型

- **SET NOCOUNT ON/OFF直接删除(v3.2.1)** — 达梦不需要，之前注释保留，现在直接整行删除

- **462个真实SQL对象端到端测试通过**(HRBI_Stage.sql, 7万行)



### 旧版功能(v3.0/v2.4.x)



- **存储过程/函数方括号替换(v3.0)** — `_post_convert_generic_types`方法，所有对象类型统一做方括号→双引号+dbo替换

- **双点号`..`替换(v3.0)** — SQL Server的`database..object`（省略dbo）→达梦`database.object`

- **数据类型正则bug修复(v3.0)** — 捕获组改为非捕获组`(?:...)`修复双重映射；suffix `[^]]`→`[^)]`修复贪婪匹配

- **UTF-16自动转换** — SQL Server导出文件常为UTF-16编码，需先用Python转UTF-8再拆分



## v2.4.5 功能 — 方括号转双引号 + dbo前缀智能处理 + 精确拆分



- **方括号→双引号(v3.5.3起)** - `[schema].[table]` → `"schema"."table"`

 - 普通标识符/列名 `[aa]` → `"aa"` (v3.5.3起加双引号; v3.5.2及以前去[]不加双引号→`aa`)

 - SQL类型名 `[nvarchar]` → `nvarchar`（去掉方括号+类型映射，不加双引号）

 - 支持30+种SQL Server类型名识别

- **dbo替换规则(v3.5.6最终版)** — 三段式和两段式区别处理:

  - **三段式**（已有schema）: dbo直接删除。`[HRBI_Stage].[dbo].[Users]` → `"HRBI_Stage"."Users"` / `HRBI_Stage.[dbo].[Users]` → `HRBI_Stage."Users"`

  - **两段式**（无schema）: dbo替换为schema_prefix。`[dbo].[Users]` → `"hrbi_stage"."Users"` / `dbo.Users` → `hrbi_stage.Users`

  - **双点号** `xxx..yyy` → `xxx.yyy`（SQL Server省略dbo的写法）

  - **正则顺序（不可颠倒）**: 1)双点号→2)三段式引号→3)三段式裸名→4)三段式混合→5)两段式引号→6)两段式裸名

  - ⚠️ 改完dbo规则后必须跑全量312过程验证

 - 支持双引号包裹格式：`"dbo"."Users"` 和裸名格式：`dbo.Users` 均可正确匹配

- **精确拆分增强** - 无明确终止符时的兜底逻辑

 - 新增`_find_next_create`函数：当找不到`;`或`GO`终止符时，用下一个`CREATE`关键字作为对象边界上界

 - 跳过字符串和注释内的`CREATE`，只匹配真正的CREATE语句开头

 - 所有对象类型（table/view/procedure/function/trigger/index/constraint）均有兜底

- **VARCHAR CHAR语义后处理** - `VARCHAR(n)` → `VARCHAR(n CHAR)`

 - 修复detokenize中类型名映射绕过CHAR语义的问题



- **重写达梦数据库转换器** - 完全重写 dm_converter.py

  - token化保护: 字符串/注释替换为占位符后再做正则替换，避免误改字符串内容

  - 按对象类型独立转换: procedure/function/view/trigger/table/index/constraint

  - 40+种数据类型映射, 30+种函数映射

  - 变量语法转换: @var -> var, DECLARE @var -> var, SET @var= -> var:=

  - TRY-CATCH -> EXCEPTION WHEN OTHERS THEN

  - 全局变量转换: @@ROWCOUNT -> SQL%ROWCOUNT

  - 触发器伪表: inserted/deleted -> NEW/OLD

  - 转换结果输出到子目录: output_split_dm/

- **拆分后转换集成** - split_sql_v21.py 新增 convert_to 参数

  - 拆分完成后自动调用转换器，按对象类型独立转换

  - 生成达梦版合并脚本 merge_all.sql

- **CLI参数** - split_sql_v22.py 新增 --convert-to dm

- **29个转换单元测试** - test_dm_converter.py 全部通过

- **修复已知bug**:

  - INSERT INTO 不再被误替换为 INTEGERO

  - token_map 合并避免占位符还原丢失

  - content = new_content 遗漏导致变量@替换无效

  - 嵌套括号 VARCHAR(100) 导致参数列表正则截断

  - 终止符 / 不再重复添加



### 过滤特定对象类型（只提取存储过程等）



拆分默认输出所有对象类型。如果只需要某类对象（如只要存储过程），有两种方式：



**方式1：拆分后拷贝（推荐，最简单）**

```bash

# 先完整拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver



# 再只拷贝目标类型到独立目录

cp output_dir/proc_*.sql /path/to/output_proc/

```



**方式2：拆分后删除不需要的文件**

```bash

# 保留存储过程，删除其他

cd output_dir && ls -1 | grep -v '^proc_' | xargs rm

```



- **⚠️ 拆分→转换→过滤的完整工作流(v3.5.4)** — 正确顺序：(1)拆分 `split_sql_v21.py input.sql output_split` (2)转换 `batch_convert.py output_split output_split_dm schema_prefix` (3)过滤 `cp output_split_dm/proc_*.sql output_final/`。**关键坑**：如果先过滤再转换，`batch_convert.py` 只能转换过滤后的文件，schema_prefix可能不对（文件名变了）；如果转换后新旧文件混在同一目录，`cp` 可能把旧文件也拷进去。**必须先转换再过滤，过滤目录必须清空**。转换后验证：`grep -rl '\bdbo\.' output_split_dm/` 应为0



### 达梦转换使用方法



```bash

# 拆分SQL Server文件并转换为达梦数据库语法

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v22.py input.sql output_dir --dialect sqlserver --convert-to dm



# 仅转换(不拆分)

python3 -c "from dm_converter import convert_sqlserver_to_dm; print(convert_sqlserver_to_dm('SELECT GETDATE()', 'generic'))"

```



### 质量报告使用方法



```python

# 单对象报告

from dm_converter import DMConverter

from report_generator import ConversionReportGenerator



converter = DMConverter()

result = converter.convert(sql_content, 'procedure', schema_prefix='hrbi')

report = ConversionReportGenerator.generate_single(result, 'sp_test', 'procedure')

print(report.to_markdown())

print(f'兼容性评分: {report.score}/100')



# 批量报告

batch = ConversionReportGenerator.generate_batch(results, schema_prefix='hrbi')

batch.save_html('report.html')    # 暗色主题HTML

batch.save_json('report.json')    # 结构化JSON

batch.save_markdown('report.md')  # Markdown



# 快速评分(不生成报告)

score = ConversionReportGenerator.quick_score(result)

```



### 批量转换(推荐，用脚本文件)



v21不支持`--convert-to`参数，需分两步：**拆分→转换→过滤**。详见 [拆分+转换完整工作流](references/split-convert-workflow-20260627.md)。



⚠️ **拆分≠转换**: 拆分产出的是原始SQL Server语法文件，必须经过`batch_convert.py`转换才能得到达梦语法。只拆分不转换是常见错误。



```bash

# 1) 拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver



# 2) 批量转换(写脚本文件方式)

cat > /tmp/batch_convert.py << 'PYEOF'

#!/usr/bin/env python3

import os, sys

sys.path.insert(0, '/Users/a1234/.hermes/skills/sql-splitter/scripts')

from dm_converter import convert_sqlserver_to_dm



src_dir = sys.argv[1] if len(sys.argv) > 1 else 'output_dir'

dm_dir = sys.argv[2] if len(sys.argv) > 2 else src_dir + '_dm'

schema_prefix = sys.argv[3] if len(sys.argv) > 3 else os.path.basename(src_dir).replace('_split','')

os.makedirs(dm_dir, exist_ok=True)



ok = err = 0

err_list = []

for f in sorted(os.listdir(src_dir)):

    if not f.endswith('.sql') or f == 'merge_all.sql': continue

    obj_type = f.split('_')[0]

    type_map = {'proc':'procedure','func':'function','trig':'trigger',

                'view':'view','table':'table','idx':'index','uidx':'index',

                'con':'constraint','seq':'sequence'}

    mapped_type = type_map.get(obj_type, 'generic')

    with open(os.path.join(src_dir, f)) as fh: c = fh.read()

    try:

        converted = convert_sqlserver_to_dm(c, mapped_type, schema_prefix=schema_prefix)

        with open(os.path.join(dm_dir, f), 'w') as fh: fh.write(converted)

        ok += 1

    except Exception as e:

        err += 1; err_list.append(f'{f}: {str(e)[:120]}')



print(f'转换完成: {ok} 成功, {err} 失败')

if err_list:

    for e in err_list[:15]: print(f'  - {e}')

PYEOF



python3 /tmp/batch_convert.py /path/to/output_dir /path/to/output_dir_dm schema_prefix

```



### 转换规则



| 类别 | SQL Server | 达梦 |

|------|-----------|------|

| 标识符 | `[aa]`(列名) → `"aa"`(去[]加双引号)/`[hrbi].[xxx]`(schema/表名) → `"hrbi"."xxx"`(去[]加双引号)/`[nvarchar]`(类型名) → `nvarchar`(去[]不加双引号，后续做类型映射) |

| 声明 | CREATE PROCEDURE ... AS | CREATE OR REPLACE PROCEDURE ...(p1 INT) **AS** |

| 数据类型 | INT/BIT/DATETIME/MONEY/NVARCHAR/VARCHAR/UNIQUEIDENTIFIER | INTEGER/BOOLEAN/TIMESTAMP/DECIMAL(19,4)/VARCHAR(n CHAR)/CHAR(36) |

| 函数 | GETDATE()/ISNULL()/LEN()/CONVERT() | CURRENT_TIMESTAMP/NVL()/LENGTH()/CAST() |

| 变量 | @var / DECLARE @var / SET @var= | var / var / var:= |

| 异常 | BEGIN TRY...END TRY BEGIN CATCH...END CATCH | BEGIN...EXCEPTION WHEN OTHERS THEN...END; |

| 事务 | COMMIT TRANSACTION / ROLLBACK TRANSACTION | COMMIT / ROLLBACK |

| 全局变量 | @@ROWCOUNT / @@ERROR | SQL%ROWCOUNT / SQL%ERROR_CODE |

| 触发器 | inserted/deleted | NEW/OLD |

| 终止符 | GO | / |

| 清表 | TRUNCATE TABLE xxx | DELETE FROM xxx |

| 建临时表 | SELECT * INTO #tmp FROM src | CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT * FROM src |

| DDL结尾 | 过程体DDL无分号 | 自动补分号(单行/跨行/CTAS/ON PRIMARY均处理, 仅PROC/FUNC/TRIG) |



### 输出目录结构



```

input_split/ ← 原始拆分结果

├── proc_sp_test.sql

├── table_users.sql

├── view_v_users.sql

└── merge_all.sql



input_split_dm/ ← 达梦转换版本

├── proc_sp_test.sql

├── table_users.sql

├── view_v_users.sql

└── merge_all.sql

```



### 支持的对象类型转换



| 对象类型 | 转换策略 |

|---------|---------|

| 存储过程 | CREATE OR REPLACE + **AS** + 参数@去除 + 参数加括号 + 终止符/ |

| 函数 | CREATE OR REPLACE + RETURN + IS + 终止符/ |

| 视图 | CREATE OR REPLACE + SCHEMABINDING去除 |

| 触发器 | CREATE OR REPLACE + inserted/deleted->NEW/OLD + 终止符/ |

| 表 | IDENTITY保留 + 表选项去除 + 类型映射 |

| 索引 | CLUSTERED/NONCLUSTERED去除 + INCLUDE去除 |

| 约束 | WITH NOCHECK去除 |



### 转换器核心设计要点（开发调试血泪史）

- **token化保护**: 字符串/注释替换为占位符后再做正则替换，避免误改字符串内容

- **token_map合并**: Step2对象类型转换后重新tokenize时，必须合并旧token_map，否则`__TOKEN_0__`等占位符还原丢失

- **变量@前缀**: 在token还原后再做，且用`_tokenize_strings_only`只保护字符串(不保护注释，注释里@变量也要转)

- **`content = new_content` 不可省略**: re.sub后必须更新content变量，否则后续替换基于旧文本

- **嵌套括号**: `VARCHAR(100)`中的`)`会截断`[^)]*`，参数列表匹配需用`(\([^)]*(?:\([^)]*\)[^)]*)*\))`匹配嵌套

- **数据类型上下文**: 前缀需包含`DECLARE\s+`，否则`DECLARE @v DATETIME`中的DATETIME不会被转换

- **INSERT INTO误匹配**: `INSERT INT`被匹配为前缀`\n`+列名`INSERT`+类型`INT`，需在数据类型替换中排除SQL关键字作为列名

- **⚠️ 多正则顺序执行重复匹配陷阱(v3.3.0修复)**: `_convert_procedure`中三个re.sub顺序处理同类模式(有括号参数/无括号参数/无参数存储过程)，第一个替换后的结果被后续正则再次匹配，导致引号叠加`""sp_test""`。**绝不可用多个re.sub顺序处理同一token的不同形式**——必须用单一正则+分支回调，确保每个模式只被匹配一次。详见 [v3.3.0修复记录](references/dm-converter-v330-fixes.md)

- **⚠️ 添加新转换规则的流程**: 在dm_converter中添加新规则（如TRUNCATE→DELETE）的标准流程：(1)在`_convert_statements`或新增专用方法中实现逻辑 (2)在convert() Step4方法链中调用 (3)在test_dm_converter.py添加测试(至少3个:基本转换+大小写+上下文) (4)跑全部测试确认无回归 (5)更新SKILL.md转换规则表和更新日志 (6)更新wiki页面

- **⚠️ 不用delegate_task做代码审查**: dm_converter.py有2265行，delegate_task子任务逐行审查会超时(600s)。正确做法：主agent直接用search_files+read_file定位关键函数，逐条验证规则覆盖，手动补缺失。delegate_task适合独立可并行的任务，不适合需要大量file I/O的逐行审查

- **⚠️ IDENTITY插入位置陷阱(v3.3.0修复)**: SQL中`)`出现在很多上下文(列类型`VARCHAR(100)`、函数调用、表定义结束)。匹配表结束的`)`必须用上下文锚定(独占一行`^(\s*)\)(\s*$)`或紧跟`;`/换行)，不能简单匹配第一个`)后行尾`——会匹配到列定义中的嵌套`)`。详见 [v3.3.0修复记录](references/dm-converter-v330-fixes.md)

- **⚠️ token_map占位符key碰撞(v3.3.0修复)**: Step3重新tokenize时counter从0开始，新占位符`__TOKEN_0__`覆盖了Step1中同key的原始内容，导致字符串`'N/A'`被还原为标识符`"v_users"`。修复：`_tokenize`新增`start_counter`参数，Step3传入`max(已存在key)+1`。**任何生成占位符的系统重新运行时，必须从已存在key的最大值+1开始**。详见 [v3.3.0修复记录](references/dm-converter-v330-fixes.md)

- **⚠️ 方括号替换规则变更(v3.5.3)**: `_convert_bracket_identifiers()`从v3.5.3起区分类型名和非类型名：非类型名`[aa]`→`"aa"`(加双引号)，类型名`[int]`→`int`(不加双引号，后续做类型映射)。判断逻辑：取方括号内首段token，与TYPE_MAPPINGS键集合(小写)匹配。**注意**：`[int identity]`这种含后缀的方括号内容，只有首段`int`参与类型名判断，整个内容会被保留为裸名(`int identity`不加双引号)——这是正确行为，因为这种写法只出现在列定义中，后续类型映射会处理。v3.5.2及以前所有方括号内容都不加双引号(→裸名)，需要额外`_post_convert_generic_types`步骤加双引号

- **⚠️ _quote_name先split再去方括号(v3.3.0修复)**: `_quote_name`先检查整体`[...body...]`格式，但`[dbo].[PROC_xxx]`以`[`开头`]`结尾被误当成单个方括号标识符，去首尾后变成`dbo].[PROC_xxx`。**当输入可能是schema.name格式时，必须先split('.')再逐段去方括号/引号**，绝不能先对整体做去除外层处理。详见 [v3.3.0修复记录](references/dm-converter-v330-fixes.md)

- **⚠️ `##`全局临时表替换顺序(v3.3.0修复)**: `re.sub(r'#(\w+)',...)`对`##GlobalTemp`只替换第二个`#`变成`#tmp_`。修复：先替换`##`→`gtmp_`再替换`#`→`tmp_`。详见 [v3.3.0修复记录](references/dm-converter-v330-fixes.md)

- **⚠️ TRUNCATE TABLE在达梦存储过程内不支持(v3.4.0)**: 达梦不支持在存储过程内使用TRUNCATE TABLE，`_convert_truncate`自动将`TRUNCATE TABLE xxx` → `DELETE FROM xxx`。注意：DELETE FROM没有TRUNCATE的重置IDENTITY/不写日志等语义差异，但达梦存储过程内只能用DELETE

- **⚠️ _ensure_ddl_semicolons状态机实现要点(v3.4.5)**: 达梦存储过程体内DDL必须以`;`结尾。用状态机扫描过程体：(1)单行DDL如`DROP TABLE xxx`直接补`;` (2)跨行建表用括号深度跟踪，在`)`行补`;` (3)CTAS跨行子查询遇到下一个语句开头时回补`;` (4)ON PRIMARY文件组语法去掉后补`;`。**关键：此步骤仅对PROCEDURE/FUNCTION/TRIGGER执行**，TABLE/VIEW等独立对象由Step9处理。312个存储过程DDL缺分号从447降为0

- **⚠️ SELECT INTO #临时表达梦不支持(v3.4.5)**: 达梦不支持`SELECT ... INTO #新表 FROM ...`建表语法。`_convert_temp_tables`自动将临时表的SELECT INTO转为CTAS：`CREATE GLOBAL TEMPORARY TABLE "tmp_xxx" AS SELECT ... FROM ...`。**非临时表的SELECT INTO暂不自动转换**——因为与达梦变量赋值语法`SELECT expr INTO var FROM ...`形式相同，无法程序化区分。用户需手动处理非临时表的SELECT INTO（改为先CREATE TABLE再INSERT INTO ... SELECT）

- **⚠️ delegate_task不适合逐行代码审查(v3.4.5)**: 2265行dm_converter.py用delegate_task子任务逐行审查600s超时。正确做法：主agent直接用search_files+read_file定位关键函数，逐条验证规则覆盖，手动补缺失。delegate_task适合独立可并行的任务，不适合大量file I/O的审查

- **⚠️ TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE结尾必须加分号(v3.4.0)**: 之前只有PROCEDURE/FUNCTION/TRIGGER有`_add_terminator`加`/`终止符，TABLE/VIEW等DDL对象结尾没有统一加分号。新增`_add_ending_semicolon`确保这些对象以`;`结尾。拆分阶段(split_sql_v21.py)也去掉了Oracle/DM不加分号的特殊逻辑，所有方言统一加分号

- **⚠️ 函数参数链路断裂是隐蔽BUG(v3.4.5)**: `_convert_split_output()`签名有schema_prefix参数但调用处没传，`convert_sqlserver_to_dm_with_result()`也没传——函数链路每环漏传，整个功能静默失效。**教训：新增参数到函数签名时，必须grep所有调用点确保传参**，否则功能"存在但不生效"的BUG极难发现

- **⚠️ "完全拆分出X"≠"拆分所有"**：当用户说"完全拆分出存储过程"时，意思是**只提取存储过程**，不是拆分全部对象类型。应使用过滤方式（见上方"过滤特定对象类型"章节），先拆分再按前缀筛选，而非默认全量输出后让用户自己找。



- **⚠️ patch工具缩进陷阱（严重，已反复触发）**: patch工具修改Python缩进时极易出错：(1) else块内代码被放到块外 (2) if子块和if本身同缩进 (3) 修复脚本的缩进也可能不对（17空格vs16空格的1位偏差导致整个if块变成else子块）。**终极方案：涉及Python方法体修改时，不要用patch，用Python脚本替换整个方法（find方法定义起始→find return content结束→拼接新方法体）。每次修改后必须用`python3 -m py_compile file.py`验证。仅靠lint不够——py_compile才能发现缩进导致的SyntaxError/IndentationError**。详见 [v2.4.5设计记录](references/dm-converter-v245-bracket-dbo-split.md)

- **⚠️ Python缓存陷阱**: 修改.py后pytest可能运行旧的`__pycache__/*.pyc`。修改后必须`find . -name '*.pyc' -delete`或`PYTHONDONTWRITEBYTECODE=1 python3 -m pytest ...`。否则改了代码但测试结果不变，误导调试方向

- **⚠️ write_file不能写代码文件**: Hermes的write_file工具会给内容添加`NNN|`行号前缀，导致Python文件损坏。**代码文件只能用patch工具或terminal的python脚本修改**。详见 [v2.4.3修复记录](references/dm-converter-v243-fixes.md)

- **⚠️ Python脚本嵌套字符串修改dm_converter**: execute_code中用字符串拼接修改dm_converter.py会因缩进/引号嵌套报IndentationError。**正确做法**：写独立.py脚本文件到/tmp/再用terminal执行。步骤：(1)write_file写patch脚本到/tmp (2)terminal运行`python3 /tmp/patch_xxx.py` (3)py_compile验证 (4)跑测试。这是patch工具和terminal python脚本的补充方案——当patch工具做复杂多位置修改时，脚本文件更可控

- **⚠️ detokenize类型名映射陷阱**: 方括号包裹的类型名`[nvarchar]`在token保护下不会被Step4类型映射匹配，必须在detokenize还原时同时做映射+去掉方括号，否则变成`nvarchar(100)`但已过Step4不再映射。详见 [v2.4.5设计记录](references/dm-converter-v245-bracket-dbo-split.md)

- **⚠️ _convert_data_types捕获组偏移陷阱(v3.0修复)**: 正则`(\[?(TYPE_PATTERN)\]?)`中TYPE_PATTERN本身是`(INT|VARCHAR|...)`捕获组，导致type_name是group(3)而内部type是group(4)，suffix本应在group(4)却变成了group(5)。症状：`INT`映射成`INTEGERINT`、`VARCHAR`映射成`VARCHARVARCHAR`。修复：改`(TYPE_PATTERN)`为`(?:TYPE_PATTERN)`非捕获组

- **⚠️ _convert_data_types suffix贪婪匹配陷阱(v3.0修复)**: suffix正则`(\([^]]*...) used `[^]]*` (match non-`]`) instead of `[^)]*` (match non-`)`)。`[^]]*` matches everything up to a `]` which rarely appears in SQL, so `(100)` 后的所有内容全被吞进suffix。症状：第一个类型后面所有列定义都被当作suffix，后续列的类型映射全部失效。修复：`[^]]` → `[^)]`

- **⚠️ procedure/function方括号不替换(v3.0修复)**: `_post_convert_table_types`只对TABLE/VIEW做方括号→双引号+类型映射+dbo替换，PROCEDURE/FUNCTION走的是`_replace_dbo_prefix`只处理双引号格式的dbo。但procedure原始SQL是`[dbo].[xxx]`方括号格式，dbo替换匹配不到。修复：新增`_post_convert_generic_types`对所有非TABLE/VIEW类型做方括号→双引号+类型映射+dbo替换

- **⚠️ 存储过程VARCHAR(n)缺少CHAR语义(v3.2.2修复)**: 之前`_post_convert_generic_types`注释写"不做 VARCHAR(n) -> VARCHAR(n CHAR) (过程体内变量声明不需要)"，但用户要求存储过程中的VARCHAR也必须加CHAR语义，与TABLE一致。修复：(1)在`_post_convert_generic_types`中新增`VARCHAR(n) → VARCHAR(n CHAR)`替换正则 (2)DECLARE变量/参数也会被加CHAR语义

- **⚠️ CAST中nvarchar未映射(v3.2.2修复)**: `_post_convert_generic_types`原来只有`_bracket_type_pattern`(匹配方括号包裹的类型如`[nvarchar]`)，但SQL Server过程体中`cast(x as nvarchar(50))`的`nvarchar`是裸名无方括号，不匹配。修复：新增`_bare_type_pattern`用`(?<=\s)`前缀匹配裸类型名。注意：`_bare_type_pattern`必须用lookbehind `(?<=\s)`避免匹配列名(列名在逗号/括号后不会有空格前缀)

- **⚠️ PROCEDURE三正则顺序执行导致双重引号(v3.3.0修复)**: `_convert_procedure`中三个`re.sub`顺序执行，第一个`_format_bracket_params`替换后输出`PROCEDURE "sp_test" (...)`，第三个`_fmt_no_param_proc`的正则`PROC\s+(.+?)\s+AS`又匹配到了这个结果，把`"sp_test" (...)`当成name再包引号变成`""sp_test""`。修复：合并为单一正则+分支回调`_format_proc`，确保每个存储过程声明只被匹配和替换一次。**教训：多个正则顺序替换同一类语法时，后面的正则会匹配前面替换的结果——必须用单一正则或标记已替换区域**

- **⚠️ SET NOCOUNT ON/OFF带分号不匹配(v3.2.1修复)**: `_convert_statements`正则`^\s*SET NOCOUNT ON\s*$`不匹配`SET NOCOUNT ON;`（行末带分号），导致过程体内部的SET NOCOUNT ON未被转换。修复：正则加`\s*;?`兼容分号。同时用户要求**直接删除**而非注释保留，所以`SET NOCOUNT ON`/`SET NOCOUNT OFF`映射为空字符串，正则加`\n?`吃掉换行不留空行

- **⚠️ PROCEDURE三正则顺序执行导致双重引号(v3.3.0修复)**: `_convert_procedure`中三个`re.sub`顺序执行，第一个替换后输出`PROCEDURE "sp_test" (...)`，第三个正则又匹配到把`"sp_test"(...)`当name再包引号变成`""sp_test""`。修复：合并为单一正则+分支回调。**教训：多个正则顺序替换同一类语法时，后面的会匹配前面替换的结果——必须用单一正则或标记已替换区域**

- **⚠️ IDENTITY子句插入位置(v3.3.0修复)**: 达梦语法`CREATE TABLE "name" (...) IDENTITY("col", 1, 1)`。之前正则匹配到`VARCHAR(100 CHAR)`行末的`)`把IDENTITY插在了`)`前面。修复：优先匹配独占一行的`^(\s*)\)(\s*$)`，fallback到行尾`)`。**教训：表定义中列类型括号里的`)`和表结束`)`在正则中难以区分——要求结束括号独占一行或用锚点精确匹配**

- **⚠️ 临时表正则嵌套括号截断(v3.3.0修复)**: `[^)]*`遇`NVARCHAR(100)`的`)`就截断。修复：改用贪婪`(.+)`+`re.DOTALL`匹配到最后一个`)`。**教训：匹配"最后一个右括号"时，排除式模式不可靠，改用贪婪+DOTALL**

- **⚠️ `##`全局临时表替换顺序(v3.3.0修复)**: `re.sub(r'#(\w+)',...)`对`##GlobalTemp`只替换第二个`#`变成`#tmp_`。修复：先替换`##`→`gtmp_`再替换`#`→`tmp_`。**教训：替换含`##`的标识符时必须先处理双#再处理单#**

- **⚠️ token_map碰撞(v3.3.0修复)**: Step1 tokenize`'N/A'`→`__TOKEN_0__`，Step3重新tokenize`"v_users"`又分配`__TOKEN_0__`覆盖原值。修复：`_tokenize`新增`start_counter`参数，Step3从原最大key+1开始。**教训：pipeline中多次tokenize必须保证key不碰撞——传起始偏移量**

- **⚠️ _quote_name处理[dbo].[xxx](v3.3.0修复)**: `[dbo].[PROC_xxx]`整体被误当单个方括号标识符，剥首尾括号变成`dbo].[PROC_xxx`。修复：先按`.`拆分再逐段去方括号/引号。**教训：处理含`.`的标识符时，必须先拆分再清理每段括号**

- **⚠️ SET NOCOUNT ON 在过程体内部不转换**: 转换器只处理紧跟 `AS` 后的 `SET NOCOUNT ON`（转为注释）。如果 `SET NOCOUNT ON` 出现在过程体中间（如第7行），不会被转换，残留到输出中。达梦不支持该语句，需手动注释或删除。实测462对象中2个存此问题（0.43%），属已知边界case

- **⚠️ git push分支对齐**: 本地git可能在`master`分支提交，但GitHub仓库HEAD分支可能是`main`。push到`master`不更新GitHub默认展示的`main`分支，导致网页看不到最新代码。修正：`git remote show origin`确认HEAD分支 → `git checkout main && git merge master && git push origin main`

- **⚠️ GitHub API上传大文件超时**: dm_converter.py(88KB+)通过GitHub Contents API上传时，base64后请求体巨大，curl经常超时(300s+)。**推荐方式**：直接`git add && git commit && git push`，比API逐文件PUT快得多且更可靠。之前memory记录"api.github.com可达但github.com被墙"已过时——2026-06-14实测git push可正常工作。仅在git push完全不通时才fallback到API上传

- **⚠️ UTF-16编码SQL文件**: SSMS导出的SQL脚本常为UTF-16编码(带BOM)，拆分前必须先转UTF-8，否则内容被当成二进制乱码。转换命令: `python3 -c "open('out.sql','w',encoding='utf-8').write(open('in.sql',encoding='utf-16').read())"` 详见 [v3.0修复与UTF-16转换记录](references/dm-converter-v30-fixes.md)

- **⚠️ DATE类型映射重复陷阱**: 当存储过程参数类型为`DATE`时（无方括号包裹），detokenize的类型映射可能在Step4已经替换过一次`DATE→DATE`（因为DATE在达梦也是合法类型名），但如果正则边界不够精确，会把`DATE`后面的换行/空白也吃进去，导致相邻关键字拼接，如`DATE\nAS`变成`DATEDATE\nAS`或`DATEDATEAS`。根因：类型映射正则的后缀锚点需用`\b`或`(?=\s|,|\)|$)`精确截断，不能贪婪吃进换行符。**每次修改类型映射正则后，必须跑`test_dm_converter.py`验证**

- **⚠️ dbo前缀正则陷阱**: detokenize后方括号变成双引号，正则必须同时匹配`"dbo".`和`dbo.`两种格式。三段式必须在两段式之前处理，否则`schema.dbo.object`中的`dbo.object`先被两段式误匹配。详见 [v2.4.5设计记录](references/dm-converter-v245-bracket-dbo-split.md)



### 运行转换测试



```bash

cd ~/.openclaw/skills/sql-splitter/scripts

python3 -m pytest test_dm_converter.py -v

```



### 发布到 clawhub.ai



```bash

# ⚠️ 必须用绝对路径，不能用相对路径`.`

clawhub publish /absolute/path/to/skill-dir --slug sql-splitter --version X.Y.Z

# 错误: clawhub publish .  → "Error: SKILL.md required" (即使SKILL.md明明存在)

# 正确: clawhub publish /absolute/path/to/skill-dir

# ⚠️ 版本号冲突：clawhub不允许覆盖已发布版本，必须升版本号(如3.2.2→3.2.3)重新发布

# ⚠️ 如果publish成功但随后改了SKILL.md，再次publish同一版本号会报"already exists"，必须升版

### ⚠️ GitHub 推送可能失败

macOS环境下github.com经常网络不通（"Failed to connect to github.com port 443"）。clawhub发布不依赖GitHub，可以独立使用。如果需要同步GitHub，稍后重试或检查网络。

### ⚠️ 商业化路径已废弃

v3.6.0起移除了所有License/产品化相关代码（keygen.py、license_verifier.py、RSA密钥对）。sql-splitter现在是纯开源免费工具，无功能限制。references/下的产品化规划文档也已删除。

```



**注意：clawhub上可能存在同slug不同owner的技能**（如`@fish1981bimmer/sql-splitter`和`@kingaiwork/sql-splitter`），`clawhub inspect`可能报`AMBIGUOUS_SKILL_SLUG`错误。此时用`clawhub install @fish1981bimmer/sql-splitter`指定owner。



### 发布到 GitHub



```bash

cd /Users/a1234/.hermes/skills/sql-splitter

git add -A && git commit -m "vX.Y.Z: 变更说明"

# ⚠️ 确认远程主分支名！git remote show origin 查看HEAD branch

# 如果远程HEAD是main但本地在master上提交，push到master不会更新GitHub默认展示的main

# 修正: git checkout main && git merge master && git push origin main

git push origin main

```



## 支持的 SQL 方言



- MySQL

- PostgreSQL

- Oracle

- SQL Server

- 达梦 (DM)

- 通用 (Generic)



## v2.2.1 功能



- **GUI 界面** - 提供图形化界面进行 SQL 文件拆分操作

- **断点续传** - 支持记录处理进度，中断后可以继续处理

- **批量并行处理** - 支持同时处理多个 SQL 文件，提升处理速度

- **结果预览和对比** - 可视化查看拆分结果，支持与原始文件对比

- **配置文件管理** - 保存和加载常用配置，支持导入导出

- **详细错误处理** - 结构化错误信息，包含错误类型、上下文和修复建议

- **Dry-run 预览模式** - 预览拆分结果而不实际创建文件

- **安全修复** - pickle反序列化漏洞修复，检查点改用JSON序列化



## 支持的 SQL 对象类型



| 类型 | 前缀 | 说明 |

|------|------|------|

| 存储过程 | `proc_` | CREATE PROCEDURE |

| 函数 | `func_` | CREATE FUNCTION |

| 视图 | `view_` | CREATE VIEW |

| 触发器 | `trig_` | CREATE TRIGGER |

| 表结构 | `table_` | CREATE TABLE |

| 包 | `pkg_` | CREATE PACKAGE |

| 索引 | `idx_` | CREATE INDEX |

| 唯一索引 | `uidx_` | CREATE UNIQUE INDEX |

| 约束 | `con_` | ALTER TABLE ADD CONSTRAINT |

| 序列 | `seq_` | CREATE SEQUENCE |

| 同义词 | `syn_` | CREATE SYNONYM (Oracle) |

| 事件 | `evt_` | CREATE EVENT (MySQL) |

| 物化视图 | `mv_` | CREATE MATERIALIZED VIEW (PostgreSQL) |

| 类型 | `type_` | CREATE TYPE |



## v2.0 核心改进



### 边界检测重写

- 使用 **BEGIN...END 深度匹配**确定存储过程/函数/触发器边界

- 支持 IF...THEN...END IF、CASE...END CASE、LOOP...END LOOP 嵌套

- 不再依赖"下一个 CREATE 位置"做上界，**正确处理过程体内的嵌套 CREATE 语句**

- Oracle/DM: 通过 `/` 终止符定位；SQL Server: 通过 `GO` 定位

- PostgreSQL: 支持 `$$...$$` 包裹语法

- 字符串和注释内的分号/关键字不会干扰边界检测



### 依赖分析改进

- 函数调用检测改为**限定上下文模式**（:= 赋值、WHERE/HAVING 子句等），大幅减少误报

- SQL 关键字过滤表扩展到 150+ 个，涵盖内置函数、控制流、聚合等

- 自引用自动排除

- 循环依赖不再报错，按类型优先级追加



### 合并脚本方言适配

- Oracle/DM: `@@filename` + `SET DEFINE OFF`

- SQL Server: `:r filename` + `GO`

- PostgreSQL: `\i filename` + `ON_ERROR_STOP`

- MySQL: `source filename`

- 通用: 注释方式



### 架构优化

- 提取 `common.py` 共享模块：SQLDialect 枚举、对象前缀、类型优先级、关键字表

- `dependency_analyzer.py` 不再重复定义枚举，直接引用 common

- 拆分后自动调用依赖分析，生成 `merge_all.sql`

- 新增 37 个单元测试



## 使用方法



### GUI 模式（推荐）

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/gui.py

```



### 单文件拆分

```bash

# 推荐: 用 v21 (CLI稳定, 支持所有拆分功能)

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py <input.sql> [output_dir] --dialect sqlserver



# v22 目前在无GUI环境会 ImportError (SQLSplitterGUI 依赖 tkinter)

# 如需使用, 确保系统有 tkinter: apt install python3-tk / brew install python-tk

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v22.py <input.sql> [output_dir] 2>/dev/null || \

  python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py <input.sql> [output_dir]

```

### 拆分后转达梦（两步法）



```bash

# v21 不支持 --convert-to 参数, 需分两步:

# 1) 拆分

python3 ~/.hermes/skills/sql-splitter/scripts/split_sql_v21.py input.sql output_dir --dialect sqlserver

# 2) 批量转换（用 dm_converter 直接调用）

# ⚠️ 注意: 不要用 python3 -c "复杂多行脚本"，安全扫描会拦截

# 推荐写临时脚本文件再运行:

python3 /tmp/batch_convert.py  # 脚本内容见 scripts/batch_convert.py

```



**批量转换脚本**：`scripts/batch_convert.py` — 用法: `python3 scripts/batch_convert.py [src_dir] [dm_dir] [schema_prefix]`

- 自动按文件名前缀(proce→procedure, view→view, table→table等)识别对象类型

- 遍历目录逐文件调用 `convert_sqlserver_to_dm()`

- 默认参数: src_dir=HRBI_Stage_split, schema_prefix=HRBI_Stage



### UTF-16 编码文件处理

```bash

# SQL Server 导出的 .sql 文件常为 UTF-16 编码, 需先转 UTF-8:

python3 -c "

with open('input.sql','r',encoding='utf-16') as f: content=f.read()

with open('input_utf8.sql','w',encoding='utf-8') as f: f.write(content)

print(f'Converted: {len(content.splitlines())} lines')

"

# 然后用 input_utf8.sql 做拆分

```



### 批量拆分（目录）

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --batch <目录路径> [输出目录]

```



### 批量拆分（多个文件）

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --batch "file1.sql,file2.sql,file3.sql" [输出目录]

```



### 指定方言

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --dialect oracle input.sql

```



支持的方言：`mysql`, `postgresql`, `oracle`, `sqlserver`, `dm`, `generic`



### 不生成合并脚本

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --no-merge input.sql

```



### 预览结果

```bash

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --preview input.sql output_dir

```



### 检查点管理

```bash

# 列出所有检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --list



# 查看恢复进度

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --resume input.sql



# 清理旧检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --clear --days 7



# 删除检查点

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --checkpoint --delete input.sql

```



### 配置管理

```bash

# 列出所有配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --list



# 保存配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --save --name oracle --dialect oracle



# 加载配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --load --name oracle



# 导出配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --export --name oracle --export-path oracle_config.json



# 导入配置

python3 ~/.openclaw/skills/sql-splitter/scripts/split_sql_v22.py --config --import --import-path oracle_config.json --name oracle

```



### 参数说明



| 参数 | 说明 |

|------|------|

| `input.sql` | 要拆分的 SQL 文件路径（单文件模式必需） |

| `--batch` | 批量模式标志 |

| `--dialect` | 指定 SQL 方言 |

| `--no-merge` | 不生成依赖排序的合并脚本 |

| `-q`, `--quiet` | 静默模式 |

| `output_dir` | 输出目录（可选，默认：原文件名_split） |



### 运行测试



```bash

cd ~/.openclaw/skills/sql-splitter/scripts

python3 -m pytest test_dm_converter.py -v

```



### 端到端质量验证（大文件转换后）



转换完成后，建议跑10项质量检查确认残留SQL Server语法：



```bash

DM_DIR="输出目录_dm"

echo "1. 残留方括号:        $(grep -rl '\[.*\]' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "2. 残留dbo.:          $(grep -rl '\bdbo\.' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "3. 残留@@变量:        $(grep -rl '@@[A-Z]' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "4. 残留SET NOCOUNT ON:$(grep -rl 'SET NOCOUNT ON' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "5. 残留GO终止符:      $(grep -rwl '^GO$' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "6. 残留GETDATE():     $(grep -rl 'GETDATE()' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "7. 残留ISNULL:        $(grep -rl '\bISNULL(' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "8. 双重映射(INTEGERINT等): $(grep -rl 'INTEGERINT\|VARCHARVARCHAR' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "9. 双点号残留:        $(grep -rl '\.\.' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

echo "10.CREATE OR REPLACE数:$(grep -rl 'CREATE OR REPLACE' $DM_DIR --include='*.sql' 2>/dev/null | wc -l)"

```



所有计数应为0（除了第10项和第4项可能有少量边界case残留需手动处理）。



## 输出示例



假设输入文件 `myapp.sql` 包含：

- 表 `users`

- 视图 `v_users`（依赖 users）

- 存储过程 `sp_update`（依赖 users）



输出：

```

myapp_split/

├── table_users.sql

├── view_v_users.sql

├── proc_sp_update.sql

└── merge_all.sql          ← 按依赖排序的合并脚本

```



`merge_all.sql` 内容（以 Oracle 为例）：

```sql

-- [1/3] table: users

@@table_users.sql



-- [2/3] view: v_users  -- depends on: users

@@view_v_users.sql



-- [3/3] procedure: sp_update  -- depends on: users

@@proc_sp_update.sql

```



## 文件结构



```

sql-splitter/

├── SKILL.md ← 本文档
├── SKILL.md ← 本文档
├── references/

│   ├── dm-converter-design.md ← 达梦转换器设计要点

│   ├── dm-converter-v243-fixes.md ← v2.4.3 修复记录

│   ├── dm-converter-v246-fixes.md ← v2.4.6 修复记录（捕获组偏移+suffix贪婪+procedure方括号）

│   ├── dm-converter-v30-fixes.md ← v3.0 修复记录（含HRBI_Stage真实项目验证）

│   ├── dm-converter-v322-fixes.md ← v3.2.2 修复记录（PROC VARCHAR CHAR + CAST nvarchar映射）
│   ├── dm-converter-v323-fixes.md ← v3.2.3 修复记录（PROCEDURE用AS而非IS）
│   ├── dm-converter-v330-fixes.md ← v3.3.0 修复记录（双重引号+IDENTITY位置+临时表正则+token碰撞+方括号处理）│   ├── dm-converter-v330-fixes.md ← v3.3.0 修复记录（双重引号+IDENTITY位置+临时表正则+token碰撞+方括号处理）

│   ├── dm-converter-v345-fixes.md ← v3.4.5 修复记录（dbo两段式+DATETIME2排序+方括号架构+schema_prefix传参）

│   ├── dm-converter-v353-fixes.md ← v3.5.3 修复记录（方括号替换规则变更：非类型名加双引号）

│   ├── dm-converter-v340-fixes.md ← v3.4.0 修复记录

│   ├── dm-converter-v245-bracket-dbo-split.md ← v2.4.5 方括号+dbo设计记录

│   ├── split-convert-workflow-20260627.md ← 拆分+转换完整工作流 + 用户反馈理解教训
│   ├── document-maintenance.md ← 文档维护规范（删除功能时的清理清单+重复条目预防）

└── scripts/

    ├── common.py ← 共享模块（枚举、常量、工具函数）

    ├── split_sql.py ← v2.0 主拆分脚本

    ├── split_sql_v21.py ← v2.1 主拆分脚本（带错误处理+转换集成）

    ├── split_sql_v22.py ← v2.2 主拆分脚本（集成所有新功能+功能守卫）

    ├── dm_converter.py ← 达梦数据库转换器 v3.4.5

    ├── report_generator.py ← 转换质量报告生成器（兼容性评分+风险+HTML/MD/JSON）

    ├── dependency_analyzer.py ← 依赖分析器

    ├── error_handler.py ← 错误处理模块

    ├── gui.py ← GUI 界面（tkinter）

    ├── checkpoint.py ← 断点续传模块

    ├── batch_processor.py ← 批量并行处理模块

    ├── result_previewer.py ← 结果预览和对比模块

    ├── batch_convert.py ← 批量达梦转换脚本(拆分后调用)

    ├── config_manager.py ← 配置文件管理模块

    ├── test_sql_splitter.py ← 拆分单元测试（37个）

    ├── test_v21_features.py ← v2.1 功能测试

    ├── test_dm_converter.py ← 达梦转换单元测试（53个）

    └── test_v22_features.py ← v2.2 功能测试

```



## 达梦转换器已知问题（v2.4.3）



> v2.4.3 修复了 9 个核心 BUG（DATEADD参数重排、SELECT INTO、IF/WHILE控制流、PRINT等），40个测试全部通过。详见 [v2.4.3修复记录](references/dm-converter-v243-fixes.md)



**仍需手动调整的项目：**

- STRING_AGG→LISTAGG 缺少 WITHIN GROUP 子句

- STUFF→OVERLAY、REPLICATE→RPAD 语义不完全对等

- 临时表 #temp → GTT/普通表

- EXEC/EXECUTE 动态SQL → EXECUTE IMMEDIATE

- RAISERROR → RAISE_APPLICATION_ERROR

- TOP n → ROWNUM/FETCH FIRST

- MERGE/游标/WITH(NOLOCK)/IF EXISTS 等差异



**v2.4.4 已修复的映射：**

- VARCHAR(n) → VARCHAR(n CHAR)：达梦VARCHAR默认BYTE语义，必须加CHAR才等效SQL Server的字符语义

- UNIQUEIDENTIFIER → CHAR(36)：达梦用CHAR(36)而非VARCHAR(36)，UUID是定长



## v2.4.1 新功能 — 拆分自动加 OR REPLACE



- **视图和存储过程自动添加 OR REPLACE** — 拆分时对 procedure/function/view/trigger 四类对象，自动将 `CREATE` 转为 `CREATE OR REPLACE`

 - 达梦和 Oracle 环境下对象已存在时需要 `OR REPLACE`，否则会报错

 - 已有 `OR REPLACE` 的语句不会重复添加

 - 所有方言均生效（不仅限于 DM/Oracle）

 - 实现在 split_sql_v21.py 的 `obj_content` 提取后、写入文件前



## 注意事项



- 使用正则+深度匹配识别 SQL 对象边界，对极复杂嵌套语法可能有局限

- 默认 UTF-8 编码，遇到编码问题自动 replace

- 建议先备份原文件

- 批量模式会自动创建以原文件名命名的子目录

- 自动检测 SQL 方言，也可手动指定

- 同名文件自动追加序号（如 `proc_sp_init_2.sql`）



## 常见问题



### 拆分结果不正确（多个对象混在一个文件中）



**症状**：拆分后生成的文件包含多个 SQL 对象，而不是每个对象一个文件。



**原因**：原始 SQL 文件中的对象缺少分号结束符。sql-splitter 依赖分号来确定对象的结束位置。



**解决方案**：为每个 SQL 语句添加分号。例如：



```sql

-- 错误：缺少分号

Create table a(

  Id int,

  Name varchar(10)

)



Create table b(

  Id int,

  Name varchar(10)

)



-- 正确：添加分号

Create table a(

  Id int,

  Name varchar(10)

);



Create table b(

  Id int,

  Name varchar(10)

);

```



**快速修复方法**：

```bash

# 使用 sed 为每个 CREATE 语句后的空行添加分号

sed -i '' '/^Create /,/^)/s/)$/);/' input.sql

```



### 视图未被识别



**症状**：拆分后没有生成视图文件，或视图被识别为其他对象类型。



**原因**：视图语法不规范，缺少 `AS` 关键字。



**解决方案**：修正视图语法，添加 `AS` 关键字。例如：



```sql

-- 错误：缺少 AS

create view v_a

(

select * from dual

);



-- 正确：添加 AS

CREATE VIEW v_a AS

SELECT * FROM dual;

```



### 存储过程/函数未被正确拆分



**症状**：多个存储过程混在一个文件中，或产生重复文件。



**原因**：存储过程语法不规范，缺少 `AS`/`BEGIN` 关键字或分隔符。



**解决方案**：根据数据库类型修正语法：



**SQL Server**：

```sql

-- 错误：缺少 AS 和 GO

create proc p_a

(

select * from dual

);

create proc p_b

(

select * from dual

);



-- 正确：添加 AS 和 GO

CREATE PROCEDURE p_a

AS

BEGIN

    SELECT * FROM dual;

END

GO



CREATE PROCEDURE p_b

AS

BEGIN

    SELECT * FROM dual;

END

GO

```



**Oracle/达梦**：

```sql

-- 错误：缺少 IS/AS 和 /

CREATE PROCEDURE p_a

BEGIN

    SELECT * FROM dual;

END



-- 正确：添加 IS/AS 和 /

CREATE OR REPLACE PROCEDURE p_a AS

BEGIN

    SELECT * FROM dual;

END;

/

```



**MySQL**：

```sql

-- 错误：缺少 DELIMITER

CREATE PROCEDURE p_a()

BEGIN

    SELECT * FROM dual;

END



-- 正确：使用 DELIMITER

DELIMITER //

CREATE PROCEDURE p_a()

BEGIN

    SELECT * FROM dual;

END //

DELIMITER ;

```



### 产生重复文件



**症状**：拆分后生成多个内容相同或相似的文件（如 `proc_p_a.sql` 和 `proc_p_a_2.sql`）。



**原因**：对象边界检测失败，通常由以下原因导致：

- 对象之间缺少分隔符（分号、GO、/ 等）

- 对象语法不规范（缺少 AS、BEGIN 等）

- 嵌套对象语法错误



**解决方案**：

1. 检查并修正原始 SQL 文件的语法

2. 确保每个对象之间有正确的分隔符

3. 使用 `--dialect` 参数明确指定数据库类型

4. 对于复杂情况，考虑手动拆分或使用数据库工具导出



### 预检查清单



在运行 sql-splitter 之前，建议检查以下内容：



- [ ] 每个 SQL 语句都有分号结束符

- [ ] 视图包含 `AS` 关键字

- [ ] 存储过程/函数包含 `AS`/`BEGIN` 关键字

- [ ] SQL Server 对象之间有 `GO` 分隔符

- [ ] Oracle/达梦 对象末尾有 `/` 终止符

- [ ] MySQL 存储过程使用 `DELIMITER`

- [ ] 对象名称没有特殊字符或保留字冲突

- [ ] 文件编码为 UTF-8



## 文档维护规范



- 功能描述按版本倒序排列：最新版本(v3.6.2)在最前，旧版本(v2.2.1等)在后

- **更新日志必须严格按版本号降序** — 如v3.6.2→v3.6.0→v3.5.6→v3.5.0→v3.4.5→...→v1.0.0。**每次新增版本后，用`grep '^### v' SKILL.md`检查排序是否正确**

- 避免重复章节：同一功能（如达梦转换）只在一个版本章节下详细描述，其他地方引用即可

- 标题中的版本号必须与 clawhub 发布版本一致

- 更新日志保留完整历史，但主体部分只展开最新版和次新版

- **clawhub版本号冲突时**：发布后如果又改了内容，必须升版本号（如3.2.2→3.2.3）重新发布，clawhub不允许覆盖已发布版本



## 更新日志



### v3.6.0 (2026-07-04)
- **移除 License/产品化相关代码** — 删除 keygen.py、license_verifier.py、private_key.pem、public_key.pem、issued_licenses.json，License 管理功能暂不实施
- **移除产品化规划文档** — 删除 references/ 下的 sql-splitter-productization.md、sql-splitter-v350-productization.md、sql-splitter-commercialization.md
- **清理 SKILL.md 重复条目** — 删除"商业化产品化"章节、"License管理"章节、"发布前检查清单"、重复的 dbo 规则/用户规则验证条目
- **纯开源免费工具** — sql-splitter 不再有功能限制，所有用户可使用全部功能

### v3.5.6 (2026-07-04)
- **补丁: SKILL.md 修复** — 修复 clawhub 发布后 SKILL.md 被 patch 工具引入的反斜杠转义问题，清理重复条目
### v3.5.0 (2026-06-21)

- **report_generator.py** — 转换质量报告生成器（兼容性评分0-100 + 风险分级 + HTML/MD/JSON输出 + 7个测试）

- **gui.py重写** — 完整tkinter GUI（文件选择→拆分→转换→质量报告）

- **pip打包** — pyproject.toml + src/sql_splitter/ + `sql-splitter` CLI入口



### v3.4.5 (2026-06-26)

- **dbo两段式无prefix时去掉dbo** — `dbo.xxx`无schema_prefix时直接去掉dbo变成`xxx`，不再保留

- **DATETIME2→TIMESTAMP排序BUG** — 正则alternation按长度降序排列，避免DATETIME抢先匹配DATETIME2

- **方括号替换移到Step4** — 从Step 6.7(token还原后)移到Step 4(token化后)，注释/字符串已被保护不会误改

- **schema_prefix参数贯穿调用链** — split_sql_file→_convert_split_output→convert全链传参

- **新增CLI --schema-prefix和--dm参数**

- **312个存储过程转换验证通过**(HRBI_Stage=54, HRBI_DW=157, HRBI_DM=101)

- 详见 [v3.4.5修复记录](references/dm-converter-v345-fixes.md)（含dbo演进史）



### v3.4.0 (2026-06-20)

- **TRUNCATE TABLE → DELETE FROM** — 达梦不支持TRUNCATE在存储过程内，自动将`TRUNCATE TABLE xxx`转换为`DELETE FROM xxx`

- **TABLE/VIEW结尾加分号** — TABLE/VIEW/INDEX/CONSTRAINT/SEQUENCE转换后结尾自动加`;`

- **所有方言拆分后均加分号** — 之前Oracle/DM不加分号，现在统一加

- **53个单元测试全部通过**（新增9个：4个TRUNCATE转换+5个结尾分号）



### v3.3.0 (2026-06-19)

- **PROCEDURE双重引号bug修复** — 三个顺序正则导致已替换结果被再次匹配，引号叠加成`""sp_test""`。合并为单一正则+分支回调

- **IDENTITY位置修正** — `IDENTITY("id",1,1)` 从`)`前移到`)`后，符合达梦语法。同时兼容单行和多行表定义

- **#临时表正则修复** — 列定义中的`)`截断匹配，改用贪婪匹配。`##`全局临时表不再被替换为`#tmp_`

- **token_map碰撞修复** — Step3重新tokenize占位符key覆盖原key，字符串`'N/A'`变成标识符`"v_users"`。新增`start_counter`参数避免碰撞



### v3.2.4 (2026-06-14)

- **更新日志排序修正** — 所有版本严格按版本号降序排列

- **旧版日志精简** — 去掉重复子项展开，保持简洁



### v3.2.3 (2026-06-14)

- **存储过程PROCEDURE用AS而非IS** — 达梦存储过程声明用`AS`，函数用`IS`，之前PROCEDURE也用了`IS`是错误



### v3.2.2 (2026-06-14)

- **存储过程VARCHAR(n)加CHAR语义** — DECLARE变量和参数中的`VARCHAR(n)` → `VARCHAR(n CHAR)`，与TABLE转换一致

- **CAST中nvarchar→VARCHAR(n CHAR)** — `cast(x as nvarchar(50))` → `CAST(x AS VARCHAR(50 CHAR))`，之前nvarchar未映射

- **_post_convert_generic_types增强** — 新增裸类型名映射(via `_bare_type_pattern`)，之前只映射方括号包裹的类型

- **44个单元测试全部通过**(含4个新增PROCEDURE类型映射测试)

- **462个真实SQL对象端到端测试通过**(HRBI_Stage.sql, 7万行)



### v3.2.1 (2026-06-14)

- **SET NOCOUNT ON/OFF直接删除** — 之前注释保留，用户要求直接去掉(达梦不需要)

- **SET NOCOUNT ON;带分号不匹配** — 正则加`\s*;?`兼容行末分号，之前只匹配无分号的`SET NOCOUNT ON`

- **批量转换脚本** — 新增`scripts/batch_convert.py`，写脚本文件而非`python3 -c`内联(安全扫描会拦截后者)



### v3.1.0 (2026-06-13)

- **PROCEDURE参数加括号** - `CREATE PROC name @p1 INT AS` → `CREATE OR REPLACE PROCEDURE name (p1 INT) AS`

- **OR REPLACE兼容** - 正则匹配 `CREATE OR REPLACE PROC`（拆分阶段已加 OR REPLACE 的情况）

- **AS保留** - 存储过程的`AS`关键字保留为`AS`（达梦PROCEDURE用AS，函数用IS）

- **GO;兼容** - `GO;` 也被替换为 `/`（之前只匹配纯 `GO` 行）

- **DATE不再双重映射** - `_post_convert_generic_types` 中对 DATE 类型直接返回 DATE

- **VARCHAR(max)/NVARCHAR(max) → VARCHAR(4096 CHAR)** - 之前映射为TEXT，改为VARCHAR(4096 CHAR)

- **VARCHAR2类型映射** - `varchar2 → VARCHAR2`，`VARCHAR2(max) → VARCHAR2(4096 CHAR)`



### v3.0.0 (2026-06-13)

- **修复3个dm_converter核心BUG**:

  - 捕获组偏移: `_convert_data_types`正则TYPE_PATTERN用了捕获组，导致group偏移，类型双重映射(`INT`→`INTEGERINT`)。改非捕获组`(?:...)`

  - suffix贪婪匹配: `[^]]*`应为`[^)]*`，导致VARCHAR(100)后所有列定义被吞进suffix，后续类型映射失效

  - procedure方括号不替换: 新增`_post_convert_generic_types`方法，所有对象类型都做方括号→双引号+类型映射+dbo替换

- **方括号替换对所有对象类型生效** - PROCEDURE/FUNCTION/TRIGGER 也做 `[xxx]` → `"xxx"` + dbo替换

- **双点号`..`替换** - SQL Server的`database..object`（省略dbo schema）→达梦`database.object`

- **UTF-16编码支持** - SSMS导出脚本转UTF-8后拆分

- **40个单元测试全部通过**

- **462个真实SQL对象端到端测试通过**(HRBI_Stage.sql, 7万行)

- 详见 [v3.0修复记录](references/dm-converter-v30-fixes.md) | [v2.5.1修复记录](references/dm-converter-v251-fixes.md)



### v2.5.0 (2026-05-31)

- **变量命名规范** - DECLARE局部变量自动加v_前缀, 参数保持原名, 符合达梦开发规范

- **多变量DECLARE** - `DECLARE @v1 INT, @v2 VARCHAR(100)` 正确拆分为多行独立声明

- **类型映射修正** - bit->BOOLEAN, tinyint->SMALLINT (达梦无TINYINT)

- **dbo前缀替换扩展** - 存储过程/函数中的dbo.也被替换为schema前缀

- **存储过程参数格式化** - 参数换行缩进, 加括号, DECIMAL(18,2)等括号内逗号不被误拆

- **类型映射修复** - [datetime] DEFAULT等DEFAULT后缀场景也能正确映射

- **SELECT INTO变量名** - 与DECLARE声明保持一致, 自动加v_前缀



### v2.4.5 (2026-06-08)

- **方括号→双引号** - `[schema].[table]` → `"schema"."table"`，类型名`[nvarchar]` → `nvarchar`（去掉方括号并做类型映射）

- **dbo前缀智能处理** - 三段式`[HRBI].[dbo].[Users]` → `"HRBI"."Users"`(删dbo)；两段式`[dbo].[Users]` → `hrbi_stage."Users"`(用文件名替换)

- **精确拆分增强** - 无`;`/`GO`终止符时，用下一个`CREATE`关键字作为对象边界兜底

- **VARCHAR CHAR语义后处理** - 修复detokenize类型映射绕过CHAR语义的问题

- **schema_prefix自动传递** - 从源文件名自动提取前缀传给dm_converter

- **40个测试全部通过**



### v2.4.4 (2026-06-07)

- **数据类型映射调整** - 按达梦最佳实践修正

  - VARCHAR(n) → VARCHAR(n CHAR)：达梦VARCHAR默认BYTE语义，必须加CHAR才等效SQL Server的字符语义

  - UNIQUEIDENTIFIER → CHAR(36)：达梦用CHAR(36)而非VARCHAR(36)，UUID是定长

- **40个测试全部通过**



### v2.4.3 (2026-06-06)

- **达梦转换器BUG修复** - 9个失败测试全部修复，40/40通过

  - BIT→BOOLEAN, TINYINT→SMALLINT 类型映射修正

  - NVARCHAR(n) → VARCHAR(n CHAR) 达梦字符语义转换

  - SET NOCOUNT ON注释格式修正

  - DATEADD专用转换方法（参数重排：DATEADD(day,n,date) → date + INTERVAL 'n' DAY）

  - SELECT赋值区分有无FROM（有FROM→SELECT INTO，无FROM→:=）

  - IF...BEGIN...END → IF...THEN...END IF 控制流转换

  - WHILE...BEGIN...END → WHILE...LOOP...END LOOP 控制流转换

  - PRINT → DBMS_OUTPUT.PUT_LINE 转换（+号连接改||）

- 详见 [v2.4.3修复记录](references/dm-converter-v243-fixes.md) | [缩进调试技巧](references/python-indentation-debugging.md) | [7万行实战](references/hrbi-stage-real-world-test.md)



### v2.4.1 (2026-05-30)

- **拆分自动加 OR REPLACE** - 对 procedure/function/view/trigger 四类对象，自动将 CREATE 转为 CREATE OR REPLACE

  - 已有 OR REPLACE 的语句不重复添加

  - 所有方言均生效



### v2.4.0 (2026-05-23)

- **重写达梦数据库转换器** - 完全重写 dm_converter.py

  - token化保护: 字符串/注释替换为占位符后再做正则替换

  - 按对象类型独立转换: procedure/function/view/trigger/table/index/constraint

  - 40+种数据类型映射, 30+种函数映射

  - 变量语法转换: @var -> var, DECLARE @var -> var, SET @var= -> var:=

  - TRY-CATCH -> EXCEPTION WHEN OTHERS THEN

  - 全局变量转换: @@ROWCOUNT -> SQL%ROWCOUNT

  - 触发器伪表: inserted/deleted -> NEW/OLD

  - 转换结果输出到子目录: output_split_dm/

- **拆分后转换集成** - split_sql_v21.py 新增 convert_to 参数

  - 拆分完成后自动调用转换器，按对象类型独立转换

  - 生成达梦版合并脚本 merge_all.sql

- **29个转换单元测试** - test_dm_converter.py 全部通过

- 详见 [v2.4.0修复记录](references/dm-converter-v240-fixes.md)



### v2.2.1 (2026-05-01)

- **安全修复** - 修复 pickle 反序列化漏洞，替换为 JSON + 数据验证

- **新增安全文档** - 添加 SECURITY.md

- **新增依赖管理** - 添加 requirements.txt



### v2.2.0 (2026-04-27)

- **新增 GUI 界面** - 提供图形化界面进行 SQL 文件拆分操作

- **新增断点续传功能** - 支持记录处理进度，中断后可以继续处理

- **新增批量并行处理** - 支持同时处理多个 SQL 文件，提升处理速度

- **新增结果预览和对比** - 可视化查看拆分结果，支持与原始文件对比

- **新增配置文件管理** - 保存和加载常用配置，支持导入导出



### v1.1.0 (2026-04-13)

- 新增索引支持：CREATE INDEX, CREATE UNIQUE INDEX

- 新增约束支持：ALTER TABLE ADD CONSTRAINT

- 所有 6 种方言均支持索引/约束识别



### v1.0.0

- 初始版本